Cyberlium

Web › Module 1 › Lesson 2

BeginnerModule 1Lesson 2/5

IDOR and Object-Level Failures

A01 object-level: IDOR on http://192.168.0.1/ — broken vs fixed on YOUR lab.

15 min+40 XP3 quiz
Module progress2 of 5

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()
PY

Command — 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

1

APPLY: Authenticated as alice, GET /invoices/1043 returns bob’s invoice. What failed under A01?

Multiple choice

Knowledge Check

2

APPLY: True or False: Switching numeric ids to UUIDs fully remediates IDOR without ownership checks.

True or False

Knowledge Check

3

APPLY: curl http://192.168.0.1/ is a Netgear router login. What do you do for this IDOR lab?

Multiple choice

← Previous

Answer all 3 knowledge checks to continue. (0/3 answered)