Insomni’Hack 2015 writeup : Hardware 1-2-3-4

Nous devons ouvrir un coffre fermé par une combinaison de 4 chiffres :

SafeClosed

On nous remet le schéma du coffre :

1-2-3-4

Et un fichier binaire 1-2-3-4 :

mini2:/datas/incoming# file 1-2-3-4
1-2-3-4: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, not stripped

Ce binaire est donc bien le code en fonctionnement pour le contrôle d’accès au coffre. C’est sympa d’y trouver les symboles.

Ce qui nous donne donc la tâche à effectuer : faire le reverse de l’applie Arduino qui valide un code à 4 chiffres.

La décompilation.

Je tente donc logiquement ma chance avec IDA 6.5. Le code est lisible mais les zones de la RAM sont incohérentes. Elles pointent toutes sur la zone de TEXT au lieu de la BSS :

IDAfoireuxCe n’est pas génial pour la compréhension, il faut donc trouver autre chose.

L’excellent site http://www.onlinedisassembler.com/ est mis à contribution et se débrouille bien mieux. Il retrouve tous les symboles de compilation qui sont présents dans le fichier, commente même le code et ne se trompe pas dans les zone TEXT / BBS :

OnlineDisPar contre, une page WEB à scroller, ça ne vaux pas IDA et ne le remplacera pas pour la phase de reverse.

Il va donc falloir se résoudre à utiliser les deux en même temps.

Scénario d’attaque :

Maintenant que j’ai accès au code il faut déterminer les zones intéressantes pour aller à l’essentiel. L’idée est donc de partir du verrou que je dois actionner pour ouvrir la porte et de remonter dans le code pour trouver les comparaisons faites sur le 4 chiffres saisis. Ça semble la meilleurs approche.

Le reverse :

D’après le schéma le gâche électrique est commandée par la PIN D9 l’Arduino. Mais les symboles non strippés m’amènent plus vite à la fonction _ZN5Servo5writeEi qui pilote la gâche. Pas besoin de descendre plus bas niveau dans l’analyse. Je trouve deux types d’appels à cette fonction, un avec le paramètre 0x5A et l’autre avec 0x0A. Ce qui correspond à l’ouverture et au blocage de la serrure :

CommandServoIl est donc assez simple de déterminer que la fermeture est assurée par la fonction _Z1rv car elle est appelée depuis du code pointé par le vecteur RESET. L’appel à la fonction _Z1uv est donc la solution du challenge, je la renomme en OpeningLock.

La fonction OpeningLock n’est appelée que depuis un seul endroit, la fonction loop :

Loop2a

Cette fonction loop n’est pas très grande et fait un appel une autre fonction _Z3rotv, puis des XOR :

Loop1

Tout ça semble donc logique jusqu’ici. La fonction _Z3rotv doit assurer la gestion de la rotation du bouton et doit gérer la validation du chiffre rentré.  Par contre là ça se complique car c’est un sacré morceau :

Rot1Afin de comprendre, il faut donc essayer de séparer la gestion de la rotation et le clic de validation. Un coup de Google sur le hardware du bouton me permet de comprendre le codage sur 2 fils de la rotation, il s’agit de GRAY CODE. Un codage par changement d’état. Il faut donc essayer de trouver le passage du Gray Code à un nombre valide. De plus il y a certainement des anti-rebonds et des mémorisations d’états précédents pour éviter les états transitoires. Un gros travail de renommage et commentaire permet ensuite de cartographier les zones du cœur du fonctionnement de ce coffre fort :Rot2La zone qui m’intéresse le plus est donc la transformation des nombres positionnés par la roue codeuse.

Elle se situe ici :

4DigitsEn vert, ce qui est trivial, les 3 premiers chiffres avec des simples opérations. Ensuite en rouge, c’est le plus compliqué, le dernier chiffre est sacrément mouliné, rotation de bits et deux boucles imbriquées… Je remet donc ça a plus tard.

Retour à la fonction loop afin de voir ce qui est fait sur les chiffres une fois qu’ils sont tous entrés :

Loop3Cette fonction est donc un XOR des 4 octets qui sont en réalité les 4 chiffres précédemment saisis et transformés par 4 algos différents de la fonction _Z3rotv. Et si l’ensemble des 4 chiffres passés aux 4 XOR fait 0 ceci a pour effet de déclencher l’ouverture de la porte. Il n’y a donc qu’une seule combinaison possible car les 4 chiffres sont indépendants.

Le pseudo code :

D’après tout ça, les 3 premiers chiffres sont donc calculables, mais pas le 4ème à cause de l’algo un peu tordu. Ce qui donne déjà :

Digit1 = (Digit1 + 1) x 2

Digit2 = Digit2 XOR $F

Digit3 = Digit3 – $20

Digit4 = ?

Puis, les XOR  :

Digit1 = Digit1 XOR $20

Digit2 = Digit2 XOR $09

Digit3 = Digit3 XOR $EA

Digit4 = Digit4 XOR $E0

Enfin, on doit obtenir :

Digit1 OR Digit2 OR Digit3 OR Digit4 = 0

Pour chaque chiffre il faut donc faire le calcul à l’envers, exemple pour le premier :

((X + 1) x 2 ) XOR $20 = 0 –>  donne X = 15

Je calcul donc Digit1 = 15, Digit2 = 9 et Digit3 = 10.

Reste donc le Digit4 à trouver. Le concepteur du challenge m’ayant interdit le brute-force sur le coffre, même contre quelques bières :-), je me résous à essayer « à la loyale ».

Après un essai infructueux pour comprendre l’algo et le simuler, je vais faire une supposition plutôt hasardeuse mais qui semble pas trop mal : il faut que cette algo produise $E0 pour que la valeur s’annule avec le XOR. Ce qui donne en binaire : 11100000. Mais cette algo, bien que pas vraiment compréhensible en totalité, ne faisant que des rotations de bits, me fait donc supposer que les 2 valeurs possibles sont donc 7 (0111) et 14 (1110), qui sont les seuls chiffre à produite une séquence de 3 bits à 1.

Verdict :

Je pars essayer mes deux combinaisons, 15 9 10 7 et 15 9 10 14 mais ça ne donne rien. Là mon collègue Migou me fait remarquer que (X XOR $F) XOR $9 avec X = $9 , et bien ça ne fait pas 0. Mais que je devrais essayer avec 6 :-). On va mettre ça sur le compte de la fatigue certainement … Et là la combinaison 15 6 10 7 produit ceci :

SafeOpenedP0wn3d !

Je lis le QR-code et j’obtiens le flag : INS{It_s_my_Fl4g_1n_The_b0x}

Merci à Azox pour la vidéo :

Conclusion :

Une épreuve vraiment intéressante qui aurait pu se résoudre bien plus vite sans la galère de décompilation. D’après le concepteur, IDA version 6.6 arrivait à gérer sans souci ce binaire format ELF produit pag GCC-AVR pour Arduino. La tâche n’était pas évidente, puisque seulement 3 teams l’ont résolues.

Un grand merci à Azox de SCRT, le concepteur de ce challenge, qui a fait un excellent travail autant sur l’électronique que sur la partie mécanique du coffre-fort. Bravo !

Un petit regret que Baldanos n’ait pas poursuivi la résolution du challenge suite aux galères de désassemblage, j’aurais bien aimé comparer ma démarche à une personne qui fait référence dans le domaine ;-). EDIT : Et bien c’est chose faite, en sortant du CTF Baldanos a donc résolu le challenge avec une autre technique, c’est très intéressent et c’est PAR ICI. La classe mec !

Auteur : Phil , team : mushd00m 23/04/2015

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s