Aller au contenu
Retour aux défis CTF

Défi CTF

NorthSec 2025: compte rendu du défi – Distributrice NFC

Mon collègue François Caron et moi, Étienne Ducharme, nous sommes attaqués à un défi intéressant lors de la compétition de cybersécurité NorthSec 2025 (CTF).

Sur le plancher du CTF trônait une distributrice, et elle était là pour une bonne raison: elle constituait un défi à elle seule. Le défi avait été conçu par le créatif Maxime Nadeau, un concepteur de défis bien connu à NorthSec.

Quand nous avons repéré la distributrice, nous savions qu’il fallait s’y essayer. Les défis inhabituels comme celui-là sont exactement ce qui nous fait aimer NorthSec.

Nous avons trouvé la description du défi sur le portail du CTF. Il s’appelait « Gift Shop »:

La description du défi « Gift Shop » sur le portail du CTF.

Plusieurs éléments se trouvaient à l’avant de la distributrice:

  1. Le panneau de sélection, avec un écran d’affichage des messages
  2. Le lecteur de cartes NFC servant à payer un article
  3. Des tablettes de chocolat, assez pour que chaque équipe en ait une
  4. Un sac de croustilles portant un défi visuel
  5. Le panneau d’information de la distributrice, avec un numéro de soutien

L’avant de la distributrice, avec le panneau de sélection, le lecteur NFC, les tablettes de chocolat, le sac de croustilles et le panneau d’information numérotés de un à cinq.

Il y avait six drapeaux à trouver autour de la machine, mais cet article se concentre sur la façon dont nous avons résolu le défi principal: acheter une tablette de chocolat.

La carte

Les capitaines organisateurs (n’oublions pas le thème de la croisière!) ont remis une carte-clé à chaque équipe, comme l’indiquait la description du défi. Celle-ci mentionnait que la carte contenait un crédit de 1,24 $, alors que l’article coûtait 1,25 $. Il fallait manifestement trouver comment obtenir plus de crédit.

La carte-clé remise à chaque équipe.

Armés de nos Flipper Zero (dont l’un a été gagné par notre président à NorthSec 2023!), nous avons scanné la carte-clé.

Une carte « MIFARE Classic » a été détectée. La lecture a pris plusieurs minutes et n’a finalement été que partiellement réussie. Nous n’avons pu lire que 2 clés sur 32 et 1 secteur sur 16. La carte était chiffrée et le Flipper n’arrivait pas à la déchiffrer.

Le Flipper Zero signale une carte MIFARE Classic et une lecture partielle: 2 clés sur 32 et 1 secteur sur 16.

Comme prévu, nous ne pouvions pas lire les données intéressantes. Seules les informations d’en-tête de la carte étaient lisibles, et le solde ne s’y trouvait certainement pas.

Les informations d’en-tête lisibles de la carte, qui ne comprennent pas le solde.

L’attaque

Connaissant le type de carte, nous nous sommes intéressés à l’attaque MFKey32.

Cette attaque exploite une faiblesse bien connue des cartes MIFARE Classic. Le protocole de chiffrement Crypto-1 présente des vulnérabilités dans sa gestion des nombres aléatoires (« nonces ») pendant l’authentification. En capturant seulement quelques échanges entre la carte et un lecteur, ici la distributrice, on peut analyser ces nonces pour retrouver la clé de chiffrement de la carte. Bref, cette attaque permet de découvrir la clé simplement en écoutant quelques lectures entre la carte et le lecteur.

Munis de notre Flipper Zero, nous avons lancé l’attaque contre le lecteur de cartes de la distributrice. En quelques minutes, nous avions recueilli les dix nonces nécessaires. Avec cette information, nous pouvions calculer les clés manquantes et déverrouiller notre carte.

Nous avons ensuite relancé un scan de la carte avec le Flipper. Cette fois, en quelques secondes à peine, toutes les clés et tous les secteurs ont été récupérés et rendus accessibles. La structure interne complète de la carte-clé était enfin à notre portée.

Le vidage

Nous avons transféré le vidage du Flipper sur notre ordinateur et l’avons ouvert dans un éditeur de texte. Voici comment il commençait:

Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare Plus, Mifare DESFire, SLIX, ST25TB
Device type: Mifare Classic
# UID is common for all formats
UID: 20 47 B0 20
# ISO14443-3A specific data
ATQA: 00 04
SAK: 08
# Mifare Classic specific data
Mifare Classic type: 1K
Data format version: 2
# Mifare Classic blocks, '??' means unknown data
Block 0: 20 47 B0 20 F7 88 04 00 C8 06 00 20 00 00 00 24
Block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
Block 4: 08 E6 83 02 12 16 43 56 53 53 42 6F 6E 73 65 63
Block 5: 6F 75 72 73 47 69 66 74 53 68 6F 70 18 6D 25 52
Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00
Block 7: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4
Block 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 11: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4
Block 12: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[...]

Le vidage se poursuivait jusqu’au bloc 63.

Analyse

Il s’agit d’une carte MIFARE Classic 1K comptant 16 secteurs, chacun contenant 4 blocs.

Secteur no 1

Block 0: 20 47 B0 20 F7 88 04 00 C8 06 00 20 00 00 00 24 Block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Block 3: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF

Le premier secteur commence par le bloc 0, le bloc du fabricant. Viennent ensuite deux blocs de zéros, puis la queue de secteur au bloc 3. La structure de la queue de secteur est la suivante:

[KeyA (6)] [Access Bits (3)] [User Byte (1)] [KeyB (6)]

Dans le vidage, les deux clés valent « FF FF FF FF FF FF », la clé par défaut des cartes MIFARE Classic. Ce sont les deux clés que nous avions trouvées en lisant la carte chiffrée. C’est un peu comme laisser l’authentification de votre application à « admin:admin », ce que nous n’avons évidemment jamais vu.

Secteurs no 3 à no 16

Block 8: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Block 9: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Block 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Block 11: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4

Les secteurs suivants étaient tous identiques. La zone de données était vide et la queue de secteur portait la clé trouvée par MFKey32: « 03 90 F4 24 21 22 ».

Secteur no 2

Block 4: 08 E6 83 02 12 16 43 56 53 53 42 6F 6E 73 65 63 Block 5: 6F 75 72 73 47 69 66 74 53 68 6F 70 18 6D 25 52 Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00 Block 7: 03 90 F4 24 21 22 FF 07 80 69 8C D1 92 DB 27 B4

Aucun autre secteur n’étant prometteur, la clé du défi devait se trouver là.

Le bloc 7 est la queue de secteur, comme dans les autres secteurs. Nous nous sommes donc concentrés sur les blocs 4, 5 et 6. En analysant les valeurs, nous avons repéré une plage de valeurs hexadécimales (41 à 7A) correspondant à des caractères ASCII. Dans un CTF, on surveille toujours les longues suites d’ASCII.

Du septième octet du bloc 4 jusqu’au douzième octet du bloc 5, nous avons trouvé la chaîne ASCII « CVSSBonsecoursGiftShop ». Ça collait: le nom du navire de croisière du CTF et le nom du défi.

Notre vidage ressemblait donc à ceci:

Block 4: 08 E6 83 02 12 16 C V S S B o n s e c Block 5: o u r s G i f t S h o p 18 6D 25 52 Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00 Block 7: [Queue de secteur]

La méthode CTF: la force brute

À ce stade, nous avons cherché la valeur « 1.24 » parmi les octets inconnus, sans la trouver. Notre première hypothèse était que la valeur se trouverait après l’en-tête ASCII, et non avant. Notre deuxième hypothèse était qu’elle pouvait être stockée sous forme de flottant 32 bits (ce qui donnerait « 3F9E147B » en gros-boutiste). Cette valeur n’apparaissait nulle part dans le vidage.

À court d’idées, nous sommes passés des méthodes fines et polies à des méthodes plus désespérées, comme la force brute. C’est fou ce que les CTF nous font faire.

Nous avons compté 15 octets inconnus et nous nous sommes dit qu’en modifier un seul suffirait peut-être. Nous avons donc essayé d’incrémenter chaque valeur hexadécimale de 1, un octet à la fois.

Nous avons ainsi produit 15 vidages de données, chacun avec un octet incrémenté. Nous les avons chargés sur le Flipper pour pouvoir les émuler comme autant de cartes NFC virtuelles.

La gloire chocolatée

Devant la néfaste distributrice, nous avons émulé nos 15 cartes, une à une.

Chaque image était nommée d’après le bloc et l’octet modifiés.

Les quinze images de cartes émulées sur le Flipper, chacune nommée d’après le bloc et l’octet modifiés.

L’écran de la distributrice a répondu par divers messages: « Insufficient funds », « Auth failed », « Invalid card ». D’une certaine façon, c’étaient tous de bons signes.

À la septième carte, nous avons enfin vu le message espéré: « 4,96 $ ». Nous avions maintenant largement de quoi acheter la tablette de chocolat.

Nous avons mangé le chocolat, c’était délicieux, et la montée de sucre nous a vraiment aidés à continuer d’écraser des défis! FIN

Sur le chocolat, il y avait une décoration de bateau, et au verso se trouvait le meilleur:

  • Le drapeau du défi, qui nous donnait 4 points
  • Le code du coffre, à utiliser dans le méta-défi

La méthode des intellectuels

Le défi terminé, nous sommes passés à d’autres. Après la compétition, nous avons toutefois revu notre image NFC l’esprit clair.

Rappel, le secteur no 2 ressemblait à ceci:

Block 4: 08 E6 83 02 12 16 C V S S B o n s e c Block 5: o u r s G i f t S h o p 18 6D 25 52 Block 6: B8 9E 3F 32 06 08 80 B0 EE C1 06 00 00 00 00 00 Block 7: [Queue de secteur]

L’octet incrémenté qui a fait passer le solde de 1,24 à 4,96 était « 3F », que nous avions changé pour « 40 ».

En rétro-ingénierie du processus, nous avons constaté que nos hypothèses précédentes n’étaient pas si loin du compte. Le solde était bel et bien stocké sous forme de flottant 32 bits. En partant de la position de l’octet modifié et en lisant à rebours pour tenir compte du boutisme, on obtient « 3F9EB852 ».

Les calculs concordent, et l’incrémentation de « 3F » à « 40 » nous a donné 4,96 $.

La conversion en virgule flottante de 3F9EB852 et le solde qui en résulte.

En nous documentant sur la structure des nombres à virgule flottante, nous avons compris que la modification de l’octet d’exposant expliquait le bond de valeur obtenu avec une simple incrémentation de un.

Envoyez-nous un message

Seul votre courriel est requis. Choisissez un sujet, ajoutez une note, puis envoyez.

Incident en cours? Appelez la ligne 24/7 plutôt que d’attendre une réponse: +1 450 681-1681, poste 277