19
sept.
'19

Challenge Black Badge leHACK 2019: le vainqueur

Publié le 19 septembre 2019

Lors de la première édition de leHACK qui a eu lieu en juin dernier, tous les participants se sont vu remettre un badge électronique assez minimaliste. Ce badge était constitué d'une LED et de ce qui ressemblait à une antenne. Et quelques petits trucs en plus, sensiblement cachés dans le circuit imprimé.

Premières fausses pistes

Ce n'était pas le premier challenge lié au badge que je réalisais, mais celui-ci a eu tout de même son lot d'erreurs. La plus grosse fut de ne pas mettre directement, comme je l'avais fait il y a deux ans, un indice ou un lien vers le début du challenge. Du coup, tous ceux qui s'y sont collés n'ont pas vraiment su par où commencer, et bon nombre d'entre eux ont repéré l'indice placé sur le badge: "Place me somewhere and be enlightened". Aussi se sont-ils empressés de chercher où placer le badge, harcelant le staff et moi-même pour avoir un indice permettant de localiser l'endroit en question.

Et il y a ceux qui ont cru que l'antenne était une antenne NFC (il y avait de quoi s'y méprendre), et qui ont essayé de "lire" le badge avec différentes applications. Ce qui a eu pour conséquence de faire clignoter la LED, avec un motif propre au fonctionnement du NFC. Certains ont pris cela pour le début du challenge, et ont ainsi cherché à décoder la trame, qui n'était qu'une conséquence de la tentative de communication du téléphone avec le badge. Ce dernier ne répondant bien évidemment pas.

J'ai ainsi rectifié le tir via un tweet indiquant qu'il y avait un indice pour identifier l'endroit où démarrait le challenge, qu'il s'agissait d'une URL, mais que ce n'avait rien à voir avec le texte. On était déjà l'après-midi, la journée était bien entamée.

Finalement, plusieurs participants ont tiqué sur la présence de mon avatar sur le circuit imprimé, et sont allé chercher du côté de ce blog. D'autres ont juste pensé qu'il s'agissait d'une sorte de signature, là encore un manque de clareté de ma part dans la conception de ce challenge.

Les différentes étapes

Quand j'ai pensé à ce challenge black badge, je voulais qu'il y ait différentes techniques et connaissances mises en oeuvre. Pour varier les plaisirs, mais aussi pour amener les participants sur des terrains qu'ils ne connaissent pas forcément, bien qu'il n'y ait rien eu de super exotique en soit. Les étapes étaient donc les suivantes:

  • Une petite épreuve de stéganographie des familles, avec un fichier polyglote très simple (coucou Ange !) et une truc moins sympa pour trouver l'URL de l'épreuve suivante ;
  • Un peu de cryptographie, là encore rien de très difficile si l'on connaît les différents modes d'opération ;
  • De la rétro-ingénierie de firmware, avec du nRF51822 et du Micropython dedans ;
  • Une capture radio d'une émission de données, à analyser avec certains outils dont je parlais (oh surprise) dans mon talk ;
  • Une devinette finale qui imposait de m'envoyer par email tout un ensemble d'informations répondant à celle-ci.

Je ne vais pas entrer dans les détails de la résolution de ces épreuves car différents write-ups ont été écrits et publiés par les participants (liens plus bas), mais je vais élucider certains points techniques et l'origine de certaines étapes.

Tout d'abord, la seconde étape. Je voulais intégrer une épreuve de cryptographie abordable, pas techniquement très complexe mais qui joue avec la manière dont elle est mise en oeuvre plutôt que la robustesse des clés ou des algorithmes. Disons que le bruteforce c'est bien, mais l'objectif était d'amener une vulnérabilité moins triviale qu'une clé de chiffrement facilement devinable (ou factorisable), et des concepts de mathématiques rebutant le débutant. Il m'a semblé intéressant d'explorer les modes d'opération, car tout le monde connaît le mode CBC (on dit un grand "merci" aux attaques par oracles de padding), mais je me suis rendu compte que les autres modes étaient un peu délaissés. j'ai donc opté pour une épreuve basée sur le mode CTR, qui est méconnu de certains bien que très utilisé (notamment dans divers protocoles utilisant de l'AES-GCM ou de l'AES-CCM).

De même, il me semblait inévitable de réaliser une épreuve de rétro-ingénierie de firmware. Le souci, c'est que le RE d'instructions ARM est devenu mainstream et qu'avec la sortie de Ghidra, eh bien c'est devenu beaucoup plus simple. J'ai alors pensé à une fonctionnalité que j'avais découvert quand je bossais sur le développement de Radiobit: les frozen modules. Le système Micropython, que l'on trouve notamment sur le Micro:Bit, permet lors de sa compilation d'intégrer des modules complémentaires développés en Python, mais de façon native. Un outil permet alors de transposer le code python du module en question en code C, et de le compiler et de l'intégrer aux bibliothèques de base. Bien sûr, le code C contient le bytecode du module ainsi que des métadonnées, et donc toutes les informations pour désassembler et interpréter le code du module (méthode pour les furieux). Par ailleurs, le code principal de cette troisième étape en a fait buggé plus d'un: on a bien le code Python du programme principal, mais pas de trace du module crackme ! Plusieurs solutions utilisant différentes techniques ont été trouvées, dont certaines assez originales. Je vous laisse lire les write-ups pour vous faire une idée des différentes méthodes. Cette étape a été la plus difficile à résoudre, beaucoup de participants l'ayant téléchargée mais peu résolue durant l'évènement.

L'étape suivante, la quatrième donc, consistait à aller au stand de digital.security et de demander la "boîte magique", une boîte spécifiquement conçue pour utiliser l'antenne du badge pour transmettre une donnée via un signal lumineux. Seulement voilà, il y avait un hic. Ma super boîte magique n'a pas supporté le voyage, et ne voulait plus faire allumer la LED du badge. J'ai passé quelques heures à debugger le truc, sans succès, puis j'ai indiqué aux personnes du stand de renvoyer ceux qui demanderaient cette boîte vers moi (via Twitter ou e-mail). Bizarrement, personne ne m'a contacté durant l'après-midi ou dans la nuit. Le troisième niveau aura eu raison de la majorité des participants.

J'ai donc, durant les jours suivants, distribué par Twitter et autres médias l'URL de la cinquième étape: un fichier de capture radio effectué via un SDR. L'objectif était clairement d'extraire le contenu de la communication. Là encore, rien de difficile pour qui connaît le domaine, mais c'est loin d'être le cas de la majorité des hackers présents: ces derniers savent utiliser GQRX ou des outils similaires, mais quand il faut enchaîner les blocs GNURadio, c'est une autre histoire. Avec la bonne méthode d'analyse (subtilement abordée dans mon talk de leHACK), cela allait tout seul et l'extraction ne posait plus trop de problème. On trouvait alors un lien vers l'épreuve finale: la devinette de la mort.

La devinette de la mort était la suivante:

Already there when I started to hack, it is nowadays gone and will never come back. This platform was once among the bests, providing an early hacking contest and amazing tasks to solve without a rest.

Show me your abilities by telling me what this platform is. But don't yell it here, just whisper it to my ear, show me what it did look like, show me what I did look like at this time, and the black badge will no longer be mine.

Virtualabs

L'objectif était donc d'identifier une plate-forme où j'ai découvert le hacking, de me montrer à quoi elle ressemblait, et à quoi je ressemblais à cette époque (histoire de ressortir les photos dossier).

Le vainqueur

La première personne à m'avoir fourni tous les éléments, et je dis bien TOUS les éléments, est Vincent Dehors. Bravo à lui, il remporte le Black Badge leHACK 2019 ! Il y aura peut-être un visuel dans les semaines à venir, qui sait.

Write-ups

Encore bravo à vous deux d'avoir complété la majorité de ce challenge, j'espère que vous l'avez apprécié !

04
sept.
'19

Analyse forensique du crash d'un aéromodèle radio-commandé

Publié le 04 septembre 2019

Cela va faire presque deux ans que je pratique à nouveau l'aéromodélisme. Je suis tombé dedans à 14 ans, et j'avais du laisser cette activité en suspend lorsque j'entamais mes études supérieures. Il y a environ deux ans, je décidais de m'y remettre et faisais alors l'acquisition d'un avion radiocommandé thermique, car mes précédents modèles n'étaient pas terminés et avaient fini en bois de chauffage (ou presque). Tout se passait bien, jusqu'à la séance de la semaine dernière où mon avion s'est lamentablement écrasé dans un champ. C'est l'occasion de mener une petite enquête style BEA afin de trouver l'origine de l'accident.

Le crash

Je m'étais installé tranquillement sur le terrain du club (homologué DGAC), et j'ai entamé cette session de vol par la vérification du modèle. Cette étape est obligatoire, durant laquelle on effectue un ensemble de vérifications avant le vol. Les pilotes d'avions grandeurs (des "vrais" avions, les chanceux) suivent exactement le même procédé avant chaque vol. Je vérifie ainsi le bon état des servomoteurs, des connections électriques et électroniques, ainsi que l'état des commandes de gouvernes, les serrages des vis, etc. Tout est passé au crible avant la mise en route du modèle, afin d'éviter toute surprise en vol.

L'avion paré pour la session de vols

J'effectue ensuite plusieurs vols afin de m'entraîner aux atterrissages, car c'est une phase qui est relativement sensible et que je dois en particulier travailler. Oui, j'ai appris à piloter assez récemment, et ceci fait partie de l'entraînement du débutant. Tout se passe bien jusque là, mes atterrissages ne sont pas parfaits mais l'avion se pose en un morceau et en douceur à chaque fois. Après avoir usé un plein de carburant, je remplis à nouveau le réservoir et entame une autre série de vols, toujours dans l'optique d'affiner ma technique d'atterrissage.

Je pose l'avion une fois et je repars dans la foulée (un "touch and go"), j'entame une autre approche et me rend compte que cela va être difficile de poser. En effet, l'avion arrive trop haut et si je prends la décision de poser, il terminera sa course en dehors de la piste et certainement dans un sale état. Dans ce cas de figure, l'usage veut que l'on remette les gaz à fond et que l'on prenne de l'altitude, ce que je fais. Enfin, l'avion reprend de la vitesse mais reste en phase de descente. La commande de profondeur, qui gère l'inclinaison de l'avion, ne semble plus répondre. Je vois ainsi l'avion foncer plein gaz vers le sol, en légère descente, avec pour conséquence un crash assuré.

En dernier espoir et vu qu'il me restait encore un peu de marge, je décide de m'acharner sur la commande de profondeur. Et elle se remet à fonctionner ! L'avion cabre un peu brusquement et reprend de l'altitude. Malheureusement, vu que j'étais en panique j'ai un peu forcé sur la commande et du coup il risque le décrochage, et là encore c'est le crash assuré. J'essaie d'ajuster l'inclinaison en faisant piquer légèrement l'avion, mais rien n'y fait. C'est le décrochage. L'avion se met à piquer vers le sol, et je n'ai pu que couper les gaz et observer le résultat: un bruit sourd fait de bois qui craque, de toile qui se froisse et de moteur qui s'écrase dans la terre. Je ramasse les restes dans le champ voisin, je mets le tout dans la voiture et rentre chez moi.

Un avion au tapis, un !

Analyse de l'accident

Une fois à la maison, je tente de rassembler mes souvenirs. Pas facile, tout cela s'est déroulé en un temps relativement court, et je crois me rappeler de choses qui au final ne sont peut-être pas arrivées. Pendant un temps je pensais que la commande des gaz ne répondait plus, mais après quelques jours j'étais à nouveau persuadé qu'elle fonctionnait bien (cf. les faux souvenirs).

Tout comme le BEA mène l'enquête lors d'accidents ou d'incidents impliquant des aéronefs, je décidais de chercher l'origine du mien. A ce stade de l'enquête, je suspecte tout de même plusieurs organes d'être à l'origine du dysfonctionnement: le récepteur peut avoir cafouillé, les servomoteurs être tombés en panne, ou les commandes des gouvernes peuvent avoir lâché.

Première étape, analyser les commandes des gouvernes. Un problème mécanique est si vite arrivé, bien que j'ai vérifié ces dernières avant le vol. Aucune surprise de ce côté là, tout est bien fixé et rien n'a lâché. Cela ne semble pas venir de la mécanique. Je passe ensuite au récepteur, l'alimente de nouveau et vérifie la connexion avec la radiocommande. Pas de perte au niveau de cette dernière, et un servomoteur branché sur les gaz répond très bien. Je teste avec le même servomoteur l'ensemble des commandes, et tout fonctionne comme prévu. Le récepteur est donc peu susceptible d'avoir eu un défaut de fonctionnement, mais un défaut de réception n'est pas à exclure. Enfin, je reconnecte tous les servos au récepteur (à l'exception des ailes) et vérifie le fonctionnement de ces derniers. Et là, surprise ! Le servomoteur gérant la profondeur ne répond pas. Pire, lorsqu'il est actionné manuellement, il bouge et se bloque de manière intempestive !

Il ressort ainsi plusieurs hypothèses des premières constatations. La première hypothèse formulée est que le récepteur a eu des difficultés à recevoir les signaux de ma radiocommande. La seconde est que le servomoteur de la profondeur a eu un défaut de fonctionnement. La troisième est que la panne a été causée par un évènement inconnu qui n'a pas survévu au crash. Parmi ces hypothèses, la première est très peu probable car au moment de l'atterrissage, ma radiocommande était proche de l'avion. La troisième est très difficile à déterminer car elle nécessite une inspection minutieuse des débris, ce qui est plus complexe à faire sur un modèle réduit que sur un véritable avion. Imaginez, un modèle réduit n'utilise pas de tôle pour ses parois mais une simple toile tendue... Je décide donc de creuser la seconde hypothèse.

Analyse du servomoteur défectueux

Le servomoteur en question est un HS-311 conçu par Hitec. Ce type de servomoteur est peu onéreux et réputé pour être fiable, et est utilisé par de nombreux modélistes. Avant d'entrer dans les détails de l'analyse, quelques rappels sur le fonctionnement des servomoteurs.

Un servomoteur est un système électromécanique permettant de convertir une information de position (la commande transmise) en mouvement mécanique asservi (la position du pignon). Le servomoteur a donc un moteur pour provoquer le mouvement mécanique couplé à un capteur de position, généralement un potentiomètre relié aux pignons du mécanisme de réduction. Le servomoteur est ainsi capable de faire tourner le moteur dans un sens ou dans l'autre, en fonction de la position à atteindre. Son système de pignons de réduction permet d'assurer un certain couple et une résistance aux efforts.

Je décide alors de démonter intégralement le servomoteur, afin d'isoler l'électronique de la mécanique. Je mets de côté le circuit imprimé qui contrôle le moteur à courant continu, et dessoude le moteur. Dans un premier temps, je me concentre sur le moteur: je câble directement le moteur sur une batterie, et tente d'appliquer un effort sur le pignon principal. Et là, j'arrive à stopper la rotation du moteur, ce qui n'est pas du tout normal. En effet, le moteur devrait se remettre à tourner mais au lieu de cela, il reste immobile. On dirait bien que le moteur est à l'origine du problème.

Je démonte alors le moteur à coups de pince, et à l'aide d'une dose de force et de subtiles coups donnés sur l'axe arrive à le démonter intégralement. Si vous n'avez jamais vu l'intérieur d'un moteur à courant continu, les photos ci-dessous vous éclaireront.

Rotor monté sur le socle

Stator du moteur

Il s'agit d'un moteur à pinceaux (ou moteur "brushed"), qui font contact avec une bague spécifique (le commutateur) qui alimente ensuite les différentes bobines et fait tourner le rotor. Le stator quant à lui est simplement composé d'un gros aimant. Le moteur ici présent possède un rotor constitué de trois bobines, chacune ayant un contact dédié sur l'axe du rotor avec lequel les pinceaux sont en contact.

Vérifions le bon état des bobines, pour cela je mesure la résistance à l'aide d'un multimètre. Si cette résistance est non-nulle (une dizaine d'ohms) cela signifie que la bobine est opérationnelle. Si cette dernière est nulle ou infinie, cela signifie que la bobine a fondu (court-circuit) ou que le fil est rompu à un endroit. Dans mon cas, il se trouve que deux bobines ont une résistance infinie, et donc que leurs bobinages sont rompus.

La conséquence de ce défaut est triviale: pour une position spécifique du rotor du moteur, la bobine associée ne produira aucun champ magnétique et donc aucun mouvement. Ceci dit, le rotor a une certaine inertie et en temps normal celle-ci permet de passer au delà du bobinage défectueux et de garder le moteur en rotation. Cependant, si le servomoteur s'arrête sur une bobine défectueuse, cela va causer une défaillance dans le couple et potentiellement un arrêt du moteur. Autrement dit, le moteur ne redémarrera pas si ce dernier est déjà arrêté avec les pinceaux alimentant une ou deux bobines défectueuses.

Recherche de l'origine du défaut du moteur

Si l'on cherche sur Internet les spécifications du moteur, on tombe au bout d'un moment sur un moteur à l'allure très proche, le JL-RF020 de Shantou Jiali Micro Motor Co. Ltd, qui possède les mêmes dimensions et la même allure:

Vue de face du moteur trouvé dans le servo

Et voici la fiche technique du JL-RF020. Jugez de la ressemblance:

Fiche technique du moteur JL-RF020

Le moteur est ainsi annoncé avec une tension nominale de 4.5 volts et une tension maximale de 5.5 volts. Etrange, car la fiche technique du servomoteur indique que ce dernier accepte une tension d'alimentation allant de 4.8 volts à 6 volts. Cette tension d'alimentation est appliquée à chaque bobine du moteur, sachant que deux bobines sur 3 sont alimentées en permanence. A l'aide de mon multimètre, j'ai mesuré la résistance de la seule bobine restante: environ 14 ohms. Cela donne un courant maximal pour le moteur de:

I = U/R = 6.0 / (1/((1/14)+(1/14)) = 0.857 A

Chaque bobine est parcourue par un courant de 430 mA environ lorsque le servomoteur est soumis à une tension de 6 volts. Une rupture du fil de bobinage d'une bobine de moteur est très probablement due à une surintensité, ce qui a pour conséquence de faire fondre ce dernier et de couper le circuit. Comme un fusible. La résistance de la bobine étant fixe, seule une tension plus élevée que 6 volts peut provoquer une intensité supérieure à 430 mA.

La tension relevée sur l'accu NiMH utilisé dans mon avion était de 6.7 volts au moment du crash. Cette tension était relevée en temps réel et affichée sur ma radiocommande dans les informations de télémétrie. Calculons l'intensité du courant fourni au moteur:

I = U/R = 6.7 / 7 = 0.957 A

Une telle intensité est au-delà de ce que peut supporter le moteur. Si l'on regarde l'intensité maximale que peut supporter le fil du bobinage, en nous basant sur son diamètre (0.18mm), on peut se rendre compte que ce dernier tolère au maximum 430 mA, soit approximativement l'intensité parcourant le bobinage lorsque le moteur est alimenté en 6V.

Si l'on alimente le moteur avec 6.7V, l'intensité parcourant le bobinage provoque un échauffement qui peut être fatal au moteur. Cependant, le fil met du temps à chauffer et fondre, et il faut que cette intensité traverse le fil durant une certaine période de temps.

Origine de la surintensité

Tout porte à croire que le servomoteur a été victime d'une surintensité qui a provoqué la rupture d'un fil de bobinage du moteur et le blocage de ce dernier. Mais comment ce servomoteur s'est-il retrouvé alimenté par une tension supérieure à la tension maximale de fonctionnement (6V) ?

J'utilise un accumulateur NiMH ("Nickel - Metal hydride"), qui est composé de 5 cellules. Chaque cellule délivre 1.2V, soit 6V au total. Mais ça, c'est dans le meilleur des mondes. Car lorsque l'on charge une batterie NiMH, les cellules peuvent être chargées jusqu'à environ 120% de leur capacité, soit 1.44V chaque. Ce qui donne une tension de sortie de 7.2 volts, au lieu des 6 volts nominaux. De fait, l'emploi de cet accumulateur lorsqu'il est pleinement chargé augmente les risques de rupture du fil de bobinage du servomoteur provoqué par une surintensité.

Il aurait mieux fallu choisir un accumulateur de 4.8 volts (composé de 4 cellules), dont la tension maximale fournie à pleine charge est de 5.8 volts, ce qui ne risque pas d'endommager le servomoteur. Le site sur lequel j'ai acheté l'avion en question recommandait l'accumulateur de 6 volts, je n'ai pas forcément cherché à vérifier les spécifications de ce dernier et ai donc suivi les recommandations du vendeur.

Conclusion

Après investigation, le moteur du servomoteur gérant la profondeur a subi une surintensité qui a amené deux des trois bobinages du rotor à se rompre, provoquant un dysfonctionnement du servomoteur. La commande de profondeur étant très sollicitée notamment lors des phases d'atterrissage, la probabilité d'un défaut de ce servomoteur était relativement élevée.

Une pleine charge de l'accumulateur de réception a amené ce dernier à fournir une tension supérieure à la tension maximale supportée par les servomoteurs, ce qui est probablement à l'origine de la surintensité qui a causé la défaillance.

Avant cette session de vol, j'avais chargé à bloc l'accumulateur de réception bien que ce dernier ait été peu utilisé lors de la session précédente. Habituellement, l'accumulateur fournit une tension autour de 6 volts (entre 6 et 6.2 volts) car je le chargeais rarement au maximum. C'est donc ma méconnaissance du fonctionnement des accumulateurs NiMH et le fait que je n'ai pas consulté la documentation du constructeur du servomoteur qui sont à l'origine de ce crash.

Leçon apprise.

Failure is always an option
29
juin
'17

L'attaque du porte-clé intercepteur

Publié le 29 juin 2017

Lors de la quinzième édition de la Nuit du Hack, qui avait lieu ce week-end au New York Hotel de Disneyland Paris, j'ai présenté un moyen simple et pas cher de concevoir un équipement permettant de réaliser des attaques sur le protocole Bluetooth Low Energy (BLE), voire de simuler le comportement de n'importe quel équipement BLE. Je n'ai pas eu le temps d'entrer dans les détails, aussi vais-je le faire dans ce billet de blog.

Recyclage de Gablys

Cela fait quelques années que je cotoie des Gablys, des porte-clés connectés en BLE à des smartphones permettant de détecter la perte de clés ou d'objets, qui peut accessoirement être utilisé comme télécommande pour déclencher des selfies ou retrouver son smartphone égaré (une fonction de localisation inversée, en quelque sorte).

Un Gablys est assez simple: l'ensemble repose sur un transceiver de Nordic Semiconductor capable de communiquer en BLE, le nRF51822. Ce transceiver intègre un CPU ARM ainsi qu'une mémoire Flash de 256Kio, et a la particularité d'être débuggable et programmable via OpenOCD. De plus, il possède de base un bouton poussoir ainsi qu'un buzzer piezzo, qui permettent une interaction utilisateur (quoique l'on peut s'en passer selon les usages).

Les avantages du nRF51822 sont multiples:

Bref, il est dès lors possible de reprogrammer un Gablys afin de lui faire faire ce que l'on veut, à l'aide d'outils standards.

Installation des logiciels nécessaires

Pour réussir à développer et tester un firmware opensource pour Gablys, j'ai commencé par télécharger la version 11.0 du SDK de Nordic Semiconductor. Pourquoi la version 11.0 me direz-vous ? Parce que c'était celle a priori employée pour développer le firmware original de la bête. Notez que la dernière version du SDK devrait tout aussi bien fonctionner, mais il faudra prendre en compte les évolutions des fonctions exposées par ce dernier.

Tout d'abord, on télécharge la version voulue du SDK (la version 12.0 par exemple sur le site de Nordic Semiconductor. On dézippe le fichier dans un dossier prévu à cet effet:

$ unzip nRF5_SDK_12.3.0_d7731ad.zip
$ mv nRF5_SDK_12.3.0_d7731ad nrf5-sdk

Il nous faut ensuite installer la toolchain ARM et en ce qui me concerne j'ai préféré installer une version à jour plutôt que celle de ma debian, en la téléchargeant directement à partir du site d'ARM. J'en ai profité pour la décompresser dans un dossier à part, nommé toolchain:

$ cd nrf5-sdk && mkdir toolchain && cd toolchain
$ wget "https://developer.arm.com/-/media/Files/downloads/gnu-rm/6_1-2017q1/gcc-arm-none-eabi-6-2017-q1-update-linux.tar.bz2?product=GNU%20ARM%20Embedded%20Toolchain,64-bit,,Linux,6-2017-q1-update"
$ tar xvjf gcc-arm-none-eabi-6-2017-q1-update-linux.tar.bz2

Une fois la toolchain ARM décompressée, il suffit de la déclarer dans le fichier components/toolchain/gcc/Makefile.posix du SDK Nordic Semiconductor:

GNU_INSTALL_ROOT := /home/virtualabs/nrf5-sdk/toolchain/gcc-arm-none-eabi-6-2017-q1-update/
GNU_VERSION := 6.3.1
GNU_PREFIX := arm-none-eabi

Enfin, il nous faut ajouter une configuration particulière pour notre Gablys dans le fichier examples/bsp/gablys.h. Pour ce faire, il suffit de copier un fichier de configuration existant:

$ cp pca20006.h gablys.h

Nous en profitons pour ajouter une ligne dans le fichier boards.h du même dossier afin de déclarer notre board:

...

#elif defined(BOARD_N5DK1)
  #include "n5_starterkit.h"
#elif defined(BOARD_GABLYS)
  #include "gablys.h"
#elif defined(BOARD_CUSTOM)
  #include "custom_board.h"
#else
#error "Board is not defined"

...

Puis de modifier la fin du fichier afin de ne pas utiliser de quartz basse-fréquence, absent sur notre Gablys:

// Low frequency clock source to be used by the SoftDevice
#define NRF_CLOCK_LFCLKSRC      *                      \
    .source        = NRF_CLOCK_LF_SRC_RC,            \
    .rc_ctiv       = 16,                                                 \
    .rc_temp_ctiv  = 2,                                               \
   .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_250_PPM \
*

De cette manière, tous les codes exemples fonctionneront sans problème avec notre Gablys, ce dernier ne possédant pas d'oscillateur basse-fréquence. Il est alors temps de lancer notre première compilation, et voir si ces modifications portent leurs fruits.

Première compilation

Pour essayer notre configuration, nous allons tenter de compiler une application BLE sur mesure, ble_app_beacon. Pour ce faire, nous allons ajouter une configuration propre à notre Gablys, et faire en sorte de tout configurer pour que cela compile correctement.

Dans un premier temps, il nous faut créer une arborescence sur mesure:

$ cd nrf5-sdk/examples/ble_peripheral/ble_app_beacon/
$ mkdir -p gablys/s130/armgcc/
$ cp pca20006/s130/config ./gablys/s130/ -rf
$ cp pca20006/s130/armgcc ./gablys/s130/armgcc -rf

Puis nous éditons dans un premier temps le fichier Makefile situé dans le dossier gablys/s130/armgcc/, afin de déclarer notre board plutôt que celle d'origine (PCA20006). Pour ce faire, il faut changer les options de compilation -DBOARD_PCA20006 en -DBOARD_GABLYS:

# Libraries common to all targets
LIB_FILES += \

# C flags common to all targets
CFLAGS += -DSWI_DISABLE0
CFLAGS += -DSOFTDEVICE_PRESENT
CFLAGS += -DNRF51
CFLAGS += -DNRF51822
CFLAGS += -DS130
CFLAGS += -DBLE_STACK_SUPPORT_REQD
CFLAGS += -DBOARD_GABLYS
CFLAGS += -DNRF_SD_BLE_API_VERSION=2
CFLAGS += -mcpu=cortex-m0
CFLAGS += -mthumb -mabi=aapcs
CFLAGS +=  -Wall -Werror -O3 -g3
CFLAGS += -mfloat-abi=soft
# keep every function in separate section, this allows linker to discard unused ones
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin --short-enums

# C++ flags common to all targets
CXXFLAGS += \

# Assembler flags common to all targets
ASMFLAGS += -x assembler-with-cpp
ASMFLAGS += -DSWI_DISABLE0
ASMFLAGS += -DSOFTDEVICE_PRESENT
ASMFLAGS += -DNRF51
ASMFLAGS += -DNRF51822
ASMFLAGS += -DS130
ASMFLAGS += -DBLE_STACK_SUPPORT_REQD
ASMFLAGS += -DBOARD_GABLYS
ASMFLAGS += -DNRF_SD_BLE_API_VERSION=2

Et on lance la compilation avec make:

$ make
mkdir _build
Compiling file: nrf_log_backend_serial.c
Compiling file: nrf_log_frontend.c
Compiling file: app_button.c
Compiling file: app_error.c
Compiling file: app_error_weak.c
Compiling file: app_timer.c
Compiling file: app_util_platform.c
Compiling file: hardfault_implementation.c
Compiling file: nrf_assert.c
Compiling file: sdk_errors.c
Compiling file: boards.c
Compiling file: nrf_drv_clock.c
Compiling file: nrf_drv_common.c
Compiling file: nrf_drv_gpiote.c
Compiling file: nrf_drv_uart.c
Compiling file: bsp.c
Compiling file: bsp_nfc.c
Compiling file: main.c
Compiling file: RTT_Syscalls_GCC.c
Compiling file: SEGGER_RTT.c
Compiling file: SEGGER_RTT_printf.c
Compiling file: ble_advdata.c
Compiling file: ble_conn_params.c
Compiling file: ble_srv_common.c
Assembling file: gcc_startup_nrf51.S
Compiling file: system_nrf51.c
Compiling file: softdevice_handler.c
Linking target: _build/nrf51822_xxaa.out

   text        data     bss     dec     hex filename
  11808         136     616   12560    3110 _build/nrf51822_xxaa.out

Preparing: _build/nrf51822_xxaa.hex
Preparing: _build/nrf51822_xxaa.bin

Le code compile parfaitement, et le Makefile produit un fichier binaire ainsi qu'un fichier au format ihex, dont nous allons nous servir pour programmer le nRF51822 présent sur le Gablys.

Programmation du Gablys

Le Makefile standard est fait pour fonctionner avec l'outil nrfjprog, cependant il est possible de le faire fonctionner avec OpenOCD et un STLink v2 à la chinoise.

Première étape, installer OpenOCD. La version 0.10.0 tirée de Github fera l'affaire. Vous pouvez tout aussi bien l'installer via les paquets standards de votre distribution, cela devrait fonctionner de la même façon.

On modifie donc la fin de notre fichier Makefile pour y intégrer deux cibles: la première nommée flash permettant de flasher l'application, la seconde nommée flash_softdevice permettant de flasher le SoftDevice [1].

flash: nrf51822_xxaa
    @echo Flashing: $(OUTPUT_BINARY_DIRECTORY)/$<.hex
    openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "program _build/nrf51822_xxaa.hex verify" -c "reset" -c "exit"

## Flash softdevice
flash_softdevice:
    @echo Flashing SoftDevice s130 ...
    openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "nrf51 mass_erase" -c "program $(abspath ../../../../../../components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex) verify" -c "reset" -c "exit"

Ensuite, il faut connecter correctement le STLink à notre Gablys:

Puis flasher d'abord le SoftDevice, et ensuite l'application:

$ make flash_softdevice && make flash
Flashing SoftDevice s130 ...
openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "nrf51 mass_erase" -c "program /home/virtualabs/nrf5-sdk/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex verify" -c "reset" -c "exit"
Open On-Chip Debugger 0.10.0+dev-00146-g1025be36 (2017-06-16-16:35)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.246408
Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x000006d0 msp: 0x000007c0
Info : nRF51822-QFAA(build code: G2) 256kB Flash
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0xfffffffe msp: 0xfffffffc
** Programming Started **
auto erase enabled
Info : Padding image section 0 with 2112 bytes
Warn : using fast async flash loader. This is currently supported
Warn : only with ST-Link and CMSIS-DAP. If you have issues, add
Warn : "set WORKAREASIZE 0" before sourcing nrf51.cfg to disable it
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000001e msp: 0xfffffffc
wrote 110592 bytes from file /home/virtualabs/nrf5-sdk/components/softdevice/s130/hex/s130_nrf51_2.0.1_softdevice.hex in 4.979112s (21.691 KiB/s)
** Programming Finished **
** Verify Started **
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0xfffffffc
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0xfffffffc
verified 108448 bytes in 0.698049s (151.717 KiB/s)
** Verified OK **
Flashing: /nrf51822_xxaa.hex
openocd -f interface/stlink-v2.cfg -f target/nrf51.cfg -c init -c "reset init" -c "halt" -c "program _build/nrf51822_xxaa.hex verify" -c "reset" -c "exit"
Open On-Chip Debugger 0.10.0+dev-00146-g1025be36 (2017-06-16-16:35)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.246408
Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x000006d0 msp: 0x000007c0
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x000006d0 msp: 0x000007c0
** Programming Started **
auto erase enabled
Info : nRF51822-QFAA(build code: G2) 256kB Flash
Warn : using fast async flash loader. This is currently supported
Warn : only with ST-Link and CMSIS-DAP. If you have issues, add
Warn : "set WORKAREASIZE 0" before sourcing nrf51.cfg to disable it
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000001e msp: 0x000007c0
wrote 12288 bytes from file _build/nrf51822_xxaa.hex in 0.589847s (20.344 KiB/s)
** Programming Finished **
** Verify Started **
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0x000007c0
verified 11944 bytes in 0.088029s (132.503 KiB/s)
** Verified OK **

La programmation du SoftDevice et de l'application se sont bien déroulées, tout comme la vérification de la bonne écriture du code. De base, le CPU a été remis à zéro après la programmation, et si vous avez laissé votre Gablys connecté, celui-ci doit annoncer un périphérique BLE ressemblant à un beacon.

Il est temps d'aller plus loin dans le code source, afin d'implémenter quelque chose d'utile !

Firmware opensource pour Gablys

J'ai profité de l'occasion pour développer un firmware opensource pour Gablys, réimplémentant les principaux services et leurs characteristics, et se comportant presque à l'identique. Le code source de ce firmware est disponible sur Github. Ce dernier peut être compilé et flashé comme décrit précédemment.

En pratique, les services propres au Gablys sont implémentés dans le fichier gss/gss.c, dont la fonction ble_gss_init a pour rôle la création et l'initialisation de l'ensemble de ces services et des characteristics associées. A noter l'astuce avec le SDK Nordic pour déclarer un service ayant un UUID sur 128 bits:

static const ble_uuid128_t m_btn_uuid128 =
    {
       {
           0x45, 0xfa, 0x56, 0xc1, 0xfb, 0x1b, 0xc0, 0x28,
           0x96, 0xa8, 0x67, 0x18, 0x01, 0x28, 0x17, 0x4f
       }
    };

    /* ... du code ici ... */

    /* Add Gablys Button service. */
    ble_btn_uuid.uuid = GSS_BTN_SERVICE_UUID;
    err_code = sd_ble_uuid_vs_add(&m_btn_uuid128, &ble_btn_uuid.type);
    APP_ERROR_CHECK(err_code);

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                            &ble_btn_uuid,
                            &p_gss->btn_service_handle);

    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

Enfin, la stack BLE est initialisée dans le fichier main.c, grâce à la fonction ble_stack_init:

static void ble_stack_init(void)
{
    uint32_t err_code;

    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);

    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
                                                    PERIPHERAL_LINK_COUNT,
                                                    &ble_enable_params);
    APP_ERROR_CHECK(err_code);

    //Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);

    // Enable BLE stack.
    sd_ble_enable(&ble_enable_params, &app_ram_base);
    ble_enable_params.common_enable_params.vs_uuid_count = 10;
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}

C'est d'ailleurs dans cette fonction que l'on peut rajouter du code permettant d'usurper une adresse MAC, comme ci-dessous:

static void ble_stack_init(void)
{
    uint32_t err_code;
    ble_gap_addr_t address;

    address.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;
    address.addr[0] = 0xFE;
    address.addr[1] = 0xFE;
    address.addr[2] = 0xC0;
    address.addr[3] = 0x15;
    address.addr[4] = 0x4D;
    address.addr[5] = 0xDE;

    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);

    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
                                                    PERIPHERAL_LINK_COUNT,
                                                    &ble_enable_params);
    APP_ERROR_CHECK(err_code);

    //Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);


    // Enable BLE stack.
    sd_ble_enable(&ble_enable_params, &app_ram_base);
    ble_enable_params.common_enable_params.vs_uuid_count = 10;
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);

    sd_ble_gap_address_set(BLE_GAP_ADDR_CYCLE_MODE_NONE, &address);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    // Register with the SoftDevice handler module for BLE events.
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}

Une fois compilé et flashé, le nouvel équipement est visible avec l'adresse MAC DE:4D:15:C0:FE:FE, comme le montre l'exemple ci-dessous (testé avec l'exemple beacon et non le firmware opensource):

Vous vous imaginez bien à ce stade de la lecture qu'il n'est pas très difficile de recréer n'importe quel équipement Bluetooth Low Energy à partir de ce petit porte-clé, et qu'avec un peu d'imagination et de temps on pourrait mettre à mal la sécurité de quelques équipements, voire réaliser des attaques très ciblées sur des périphériques communiquant en BLE.

Ok, et quoi d'autre ?

Nous avons démontré qu'il était possible d'utiliser un Gablys comme plateforme de développement peu onéreuse, à l'aide de logiciels libres et d'équipement à bas coût (STLink v2 notamment), et de profiter des avantages de ce dernier: il est petit, peu gourmand en énergie, programmable à volonté et alimenté par une pile bouton !

Il est dès lors possible de le mettre dans une poche et de simuler un équipement existant jusqu'à utiliser la même adresse Bluetooth (MAC), disons par exemple un cadenas connecté, et d'amener une application sur smartphone à transmettre à ce faux cadenas un code confidentiel (cette dernière faisant confiance à l'adresse Bluetooth du cadenas), comme je l'ai démontré l'année dernière lors de la Nuit du Hack.

J'ai placé le SDK modifié avec les examples cités dans cet article sur un repository spécifique, si certains d'entre vous veulent tester sans tout modifier à la main.

[1] Pour plus d'information sur les SoftDevice de NordicSemiconductor, consulter la page dédiée sur le site du fondeur



Les contenus disponibles sur ce blog sont publiés sous licence Creative Commons BY-NC-SA.
Vous pouvez réutiliser tout ou partie de ces contenus à condition de citer l'auteur et l'origine, vous ne pouvez en faire une utilisation commerciale, et enfin vous devez partager tout travail ou œuvre dérivée sous les mêmes conditions — c'est-à-dire avec la même licence d'utilisation Creative Commons.