Web › Module 1 › Lesson 2
IDOR and Object-Level Failures
A01 object-level: IDOR on http://192.168.0.1/ — broken vs fixed on YOUR lab.
Visual · idor_object_reference
IDOR: GET /broken/invoices/1043 on. If that IP is a router login, STOP and OWASP Top 10:2025 A01.
Opening
IDOR is not “guessing UUIDs for fun.” It is authorization missing after authentication succeeded.
You logged in. The API knows who you are. Then: GET /api/invoices/1042. If the server returns invoice 1042 without checking invoice.owner_id == current_user.id, you have Insecure Direct Object Reference — a Broken Access Control pattern under OWASP Top 10:2025 A01. Changing 1042 to 1043 is enough when ids are sequential. Random UUIDs slow casual guessing; they do not replace an ownership check. This is ORIGINAL Cyberlium teaching — attack literacy first, then practical curls against
1. The mechanism: authenticated ≠ authorized for that object
Authentication answers “who are you?” Authorization answers “may this who touch that object?” IDOR skips the second question. The object reference is direct (numeric id, filename, account number) in URL, body, or header. The server trusts the reference. Horizontal IDOR: user A reads user B’s data. Vertical: a low-privilege user hits an admin object id. Both are A01; IDOR is the common horizontal shape in bug reports. Finding language: “Object id from client used as sole selector; no ownership or ACL check.” Fix language: “Resolve object then assert policy (owner, tenant, role) before serialize.”
Command guide
A01 BAC — The mechanism
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8765"
Command — copy this
curl -sS -m 3 -D - "$SAFE/broken/invoices/1043?user=alice" | head -n 20
2. Why hiding ids is not the fix; checks are
Switching from /invoices/1042 to /invoices/3f2a-… reduces drive-by incrementing. Attackers with another user’s link, a leak, or an export still win if the check is missing. Predictability is an aggravating factor, not the root cause. Root cause is missing authorization. Mass assignment and blind IDOR on create/update/delete are cousins: client-supplied ids or foreign keys trusted without policy. Map to OWASP Top 10:2025 A01. A valid token for A must still fail when requesting B’s objects.
Command guide
A01 BAC — Why hiding ids is not the fix; checks are
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8765"
Command — copy this
curl -sS -m 3 -D - "$SAFE/fixed/invoices/1043?user=alice" | head -n 20
3. Attacker goal: peer data or privileged objects without earning them
The attacker wants confidentiality loss (read), integrity loss (edit/delete), or privilege (act on admin objects). They change one parameter. curl -sS -D - is enough. That is why A01 stays #1 — boring requests, big impact. Defender goal: every object path asserts policy. Automated tests: alice cannot GET bob’s invoice; alice cannot DELETE bob’s invoice; low-priv cannot GET admin-report:9.
Command guide
A01 BAC — Attacker goal
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8765"
Command — copy this
curl -sS -m 3 -o /tmp/idor_b.txt -w "broken_alice_1043 %{http_code}
" "$SAFE/broken/invoices/1043?user=alice"
curl -sS -m 3 -o /tmp/idor_f.txt -w "fixed_alice_1043 %{http_code}
" "$SAFE/fixed/invoices/1043?user=alice"
curl -sS -m 3 -w "fixed_bob_1043 %{http_code}
" "$SAFE/fixed/invoices/1043?user=bob"4. How you demonstrate safely: identify DEMO, then broken vs fixed curls
GATE first: curl http://192.168.0.1/ — if the banner is TP-Link/Netgear/Huawei “Router Admin,” STOP and use
5. What you record: definition, broken vs fixed, ethics
Date (UTC). IDOR = direct object ref without authz check (A01). Asset http://192.168.0.1/ or SAFE 127.0.0.1:8765. Broken vs fixed observed with curl. Shipping: server-side ownership/ACL on every object read/write; deny by default; cross-user tests. Ethics: NEVER enumerate stranger apps; NEVER hydra the router; NEVER classmate invoices. Path: $HOME/cyberlium-lab/idor-a01-notes.txt, chmod 600. Legal: original Cyberlium — not official OWASP certification.
6. Wrong vs right: stranger IDOR enumeration vs scoped lab curls
Worked failure — same word “IDOR,” opposite target. Right never needs a foreign tenant’s objects.
Wrong
Increment invoice ids on a live SaaS or classmate API. Hydra 192.168.0.1 because it is a router. nmap the LAN. Call random UUID guessing against strangers “research.”
Right
Identify DEMO; run broken vs fixed curls on YOUR lab or 127.0.0.1:8765. Lock idor-a01-notes.txt, chmod 600. Next: SSRF as an Access Control Failure — allowlist literacy, never real cloud metadata PoCs.
Run the block on a computer you own. Two terminals: server + curls. Stop the server when done. Windows: WSL/Git Bash or py; restrict NTFS ACL if chmod is missing.
Command guide
idor_lab.sh — DEMO http://192.168.0.1/ + broken vs fixed IDOR curls
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl sudo apt install nmap sudo apt install python3
macOS:
Command — copy this
brew install nmap brew install python3
Windows: Built-in (PowerShell: Invoke-WebRequest)
Command — copy this
choco install nmap # or download https://nmap.org/download.html
Download https://python.org/downloads/
═══ COMMANDS ═══
Command — copy this
cd "$HOME/cyberlium-lab"
Command — copy this
cat > idor_lab.py << 'PY'
"""Teaching IDOR: broken vs fixed. Bind loopback; curls may use DEMO URL you own."""
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
HOST = "127.0.0.1" # bind only — never 0.0.0.0, never argv
PORT = 8765
if HOST != "127.0.0.1":
raise SystemExit("refusing non-loopback bind")
INVOICES = {
1042: {"owner": "alice", "total": 19.99},
1043: {"owner": "bob", "total": 42.00},
class Handler(BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
print("[demo]", fmt % args)
def _send(self, code: int, body: str):
data = body.encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
u = urlparse(self.path)
qs = parse_qs(u.query)
user = (qs.get("user") or ["alice"])[0]
parts = [p for p in u.path.split("/") if p]
if parts[:2] == ["broken", "invoices"] and len(parts) == 3:
inv = INVOICES.get(int(parts[2]))
return self._send(200, str(inv) if inv else "missing")
if parts[:2] == ["fixed", "invoices"] and len(parts) == 3:
inv = INVOICES.get(int(parts[2]))
if not inv:
return self._send(404, "missing")
if inv["owner"] != user:
return self._send(403, "denied")
return self._send(200, str(inv))
return self._send(
200,
"Cyberlium A01 IDOR lab. DEMO writeup: http://192.168.0.1/ "
"Routes: /broken/invoices/ID /fixed/invoices/ID?user=alice|bob",
)
print("bind", HOST, PORT)
print("demo_writeup_url http://192.168.0.1/")
print("if 192.168.0.1 is a router, curl", f"http://{HOST}:{PORT}/")
HTTPServer((HOST, PORT), Handler).serve_forever()
PYCommand — copy this
cat > a01_idor_practical.sh << 'SH' echo; echo "--- body ---"; cat /tmp/a01_broken.body; echo echo; cat /tmp/a01_deny.body; echo echo; cat /tmp/a01_allow.body; echo SH
Command — copy this
{Mission: idor-a01-notes.txt in cyberlium-lab (mode 600)
1) 0.0.1:8765 after starting idor_lab.py.2) Run a01_idor_practical.sh (broken vs fixed curls); fill $HOME/cyberlium-lab/idor-a01-notes.txt, chmod 600. 3) Ethics: no stranger enumeration, no hydra/nmap of the LAN.
Stuck? Ask Cyberlium AI Mentor
If “I cannot learn IDOR without testing a real SaaS” still feels true, ask for a hint — not an enum script. Try: "Hint only: why authn ≠ authz for object ids, which curls prove broken vs fixed on 127.0.0.1:8765, and where locked idor-a01-notes.txt lives?"
You now treat IDOR as missing object-level authorization under OWASP Top 10:2025 A01 — demonstrated with curl on YOUR lab (or SAFE loopback) with a broken vs fixed pair and locked notes. Original Cyberlium — not official OWASP certification. Next — SSRF as an Access Control Failure — allowlist thinking on YOUR lab, never unauthorized cloud metadata.
Knowledge Check
APPLY: Authenticated as alice, GET /invoices/1043 returns bob’s invoice. What failed under A01?
Multiple choice
Knowledge Check
APPLY: True or False: Switching numeric ids to UUIDs fully remediates IDOR without ownership checks.
True or False
Knowledge Check
APPLY: curl http://192.168.0.1/ is a Netgear router login. What do you do for this IDOR lab?
Multiple choice