Présentation

L’application mobile du club, développée en Flutter, propose un flux social en page d’accueil et, toutes les quinze secondes, un QR‑code destiné aux bornes de contrôle d’entrée. Le QR code encode une URL contenant quatre champs : identifiant de compte, horodatage d’expiration chiffré, nonce aléatoire et somme de contrôle. L’auteur a constaté que les captures d’écran étaient bloquées, typique du drapeau Android FLAG_SECURE, et que le code continuait de fonctionner hors ligne, ce qui indique une génération locale.

Architecture du QR code

Le processus de création se décompose en trois étapes : encodage de l’identifiant en base64url (avec le remplissage), chiffrement de l’horodatage d’expiration à l’aide d’AES‑128‑CTR, puis calcul d’une checksum à partir d’un second secret partagé. La clé de chiffrement et le secret de checksum sont stockés en clair dans un fichier journal du téléphone, ce qui a permis leur extraction. L’URL finale, après concaténation des champs, est convertie en QR code via la bibliothèque zbar.

Reverse engineering et extraction des secrets

Pour accéder au code, l’auteur a utilisé blutter afin de désassembler le bytecode Dart, jadx pour analyser les parties Java/Kotlin, et les outils du SDK Android pour inspecter le paquet. En connectant un appareil rooté via ADB sans fil, il a pu intercepter les requêtes réseau et récupérer les journaux contenant les deux secrets. La recherche a été accélérée par ripgrep, qui a permis de localiser rapidement les chaînes de texte brut dans les logs.

Implémentation et validation

Avec les secrets en main, un prototype SvelteKit a été développé pour reproduire la génération du QR code. Les premiers essais ont échoué parce que l’horodatage était exprimé en millisecondes et que l’encodage utilisait base64url au lieu du base64 standard, tout en conservant le padding. Après correction, la mise en œuvre AES a été confrontée à WebCrypto et à OpenSSL ; les vecteurs de test ont confirmé que l’algorithme était bien AES‑128‑CTR, contrairement aux tests internes qui ne révélaient pas l’erreur. La comparaison byte‑à‑byte entre les URLs générées par le prototype et celles émises par l’application officielle a montré une identité parfaite, et le décodage du QR avec zbar a confirmé la présence du même lien. Le résultat final a permis d’utiliser le QR code généré localement aux bornes du club, éliminant ainsi la contrainte du fil d’actualité social.