mercredi 11 juillet 2012

RMLL Workshop reverse, writeup

Ce post de blog va donner diverses solutions aux challenges proposés par r00tBSD pendant le workshop reverse engineering des RMLL 2012. Les solutions aux exam1, exam2, exam3, exam4 sont expliquées.
Les binaires sont téléchargeables en suivant ce lien (500ko). Ce sont des binaires linux 32 bits standards. Les exams nécessitent de trouver le bon mot de passe, à la manière des keygenme classiques des challenges que l'on trouve sur internet.

1/ exam1 - cold start
Comme a dit le formateur, cet exercice n'est présent que pour vous éviter de finir à 0 points :-).
kevin@slack:~/binaries$ ./exam1
Usage: ./exam1 key
kevin@slack:~/binaries$ ./exam1 test
Bad key
kevin@slack:~/binaries$ strings exam1
 (je coupe un peu, et on observe enfin)
øÿuô
è\þÿÿY[ÉÃ
Usage: %s key
r56GoinQ
Bad key
Good key
ÿÿÿÿ
ÿÿÿÿ
(snip snip snip encore un peu...)
main
_init
kevin@slack:~/binaries$
Sans trop réfléchir on lit les strings et on tombe sur le Usage, puis une série de caractères qui ressemblent à une clé, et les deux messages "good key/bad key". Sans plus attendre:
kevin@slack:~/binaries$ ./exam1 r56GoinQ
Good key
kevin@slack:~/binaries$

[Une anecdote à ce sujet: j'ai fait un challenge ou la clé était 'printf'. Bien qu'elle soit sous les yeux, aussi clairement indiquée, on ne pense pas à l'essayer :) ]

2/ exam2 - les moteurs sont chauds
L'exam2 est basé sur le même principe sauf que lire le binaire avec strings ne donne aucune indication valable. Il faut trouver une autre solution.
2/A/ La solution rapide
Le programme ltrace permet de tracer l'appel aux librairies. Puisque l'on se doute qu'un strcmp est fait pour comparer la clé entrée au clavier de la clé valide on regarde:
kevin@slack:~/binaries$ ltrace ./exam2 toto
__libc_start_main(0x80484a4, 2, 0xbf88a804, 0x8048590, 0x8048580
strcmp("AFc6mcw", "toto")                        = -51
puts("Bad key"Bad key
)                                  = 8
+++ exited (status 0) +++
kevin@slack:~/binaries$
La solution crève les yeux.
kevin@slack:~/binaries$ ./exam2 AFc6mcw
Good key
kevin@slack:~/binaries$

2/B/ La solution du programmeur
Il existe une variable d'environnement LD_PRELOAD qui permet de surcharger l'appel des bibliothèques. Il suffit donc d'utiliser une bibliothèque qui détourne l'usage de strcmp pour en connaître ses arguments. Un man strcmp permet de connaître la structure de la fonction pour reprendre les mêmes arguments.
kevin@slack:~/binaries$ cat hijack.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int strcmp(const char *s1, const char *s2)
{
 printf(" String 1 = %s\n String 2 = %s\n ", s1, s2) ;
}
kevin@slack:~/binaries$ gcc -shared hijack.c -o hijack.so
kevin@slack:~/binaries$ LD_PRELOAD=$(pwd)/hijack.so ./exam2 toto
 String 1 = AFc6mcw
 String 2 = toto
 Bad key
kevin@slack:~/binaries$
La solution est bien évidemment identique. WIN.

3/ exam3 - on met les mains dans le cambouis
l'exam3 va obliger à regarder de l'assembleur dixit le formateur. On sort donc gdb, et l'analyse fastidieuse commence. Nous avons droit à une mini formation de la gui de metasm qui permet de résoudre très vite cet exercice. Pour cela on télécharge metasm à l'aide de hg
kevin@slack:~$ hg clone https://code.google.com/p/metasm
 (... snip)
kevin@slack:~$ cd metasm
kevin@slack:~/metasm$ export RUBYDIR=/home/kevin/metasm/
kevin@slack:~/metasm$ ruby samples/disassemble-gui.rb
On charge alors le fichier exam3 (menu file Open), on le désassemble à l'aide de la touche 'c', puis on appuye sur espace pour obtenir le graphe de fonctions, puis menu Actions->List fonctions, et on choisit le Main du programme. On obtient cela (cliquer sur l'image pour la voir en grand):
La lecture du binaire est plus aisée. Le premier bloc doit compter le nombre d'arguments. Le second bloc à gauche fait un strlen et si le résultat diffère de 9 part à la fonction loose (avec un nom pareil on se doute qu'il ne faut pas aller là-bas :) ), on sait donc déjà que le mot de passe doit faire 9 caractères première information.

Ensuite, on peut zoomer à l'aide de CTRL molette souris, et en vue d'avion on observe cela:
(oui les cadres sont blanc, je n'ai pas de police à la bonne taille apparemment).
On se doute donc immédiatement que le programme va comparer caractère après caractère la clé entrée par l'utilisateur.
Suivons donc les blocs pas à pas:
cmp al, 41h. 
cmp signifie comparer. En code hexa, 41 est le 'A'. Nous savons donc que le A est le premier des 9 caractères du mot de passe. 
Le bloc suivant est beaucoup moins clair, c'est sans doute lié à une optimisation compilateur (?)
On va finalement sortir gdb. Tout d'abord, pour connaître l'adresse de ce cmp, on clique dessus, puis on presse la barre espace, ce qui nous amène à:
où l'on lit donc l'adresse: 0x08048521.
Nous allons donc placer un breakpoint à cette adresse, et étudier les registres edx et eax à ce moment là:
kevin@slack:~/binaries$ gdb exam3
 (snip)
(gdb) b *0x08048521
Breakpoint 1 at 0x8048521
(gdb) run Aaaaaaaaa
Starting program: /home/kevin/binaries/exam3 Aaaaaaaaa

Breakpoint 1, 0x08048521 in main ()
(gdb) info registers
eax            0x44    68
ecx            0x4c504300    1280328448
edx            0x61    97
ebx            0xb7fc2ff4    -1208209420
esp            0xbffff0e0    0xbffff0e0
ebp            0xbffff0f8    0xbffff0f8
esi            0x0    0
edi            0x0    0
eip            0x8048521    0x8048521
eflags         0x206    [ PF IF ]
cs             0x73    115
ss             0x7b    123
ds             0x7b    123
es             0x7b    123
fs             0x0    0
gs             0x33    51
(gdb)
Nous devons lancer le programme avec une clé de 9 caractères débutant par un A majuscule sinon nous sortirons avant. Une fois breakpointé, nous consultons les registres: edx vaut 0x61, ce qui correspond au a minuscule. eax vaut 0x44 qui est le D majuscule. Le deuxième caractère du mot de passe est donc le D majuscule.

En progressant de la même manière, il est possible de retrouver les 8 premiers caractères de la clé. huit? que 8? Or nous savons qu'il en faut neuf!
Petite blague du formateur, il ne faut pas chercher un 9e caractère ou une instruction oubliée. En effet, ce programme ne vérifie que les 8 premiers caractères d'une clé qui en contient 9. Ce qui signifie que n'importe quelle caractère en 9 position est valide!
kevin@slack:~/binaries$ ./exam3 AD13=xhca
Good key
kevin@slack:~/binaries$ ./exam3 AD13=xhcb
Good key
kevin@slack:~/binaries$ ./exam3 AD13=xhc9
Good key
kevin@slack:~/binaries$

[info pratique: sous linux man ascii affiche le code ascii des caractères]

4/ exam4 - sortie de piste!!
Pour cet exercice, je pense que toute la salle s'est fait berner. On est chaud, on a le gdb dégainé, le metasm-gui ouvert, donc on regarde direct le programme. Sauf que là, on a deux surprises:
Tout d'abord un graphe pas très engageant, et ensuite une absence totale de fonction main...
La solution est à portée de main. Le programme est en fait packé, il faut le dépacker pour pouvoir l'analyser. Un simple hexdump donne une première idée, un strings confirme:
kevin@slack:~/binaries$ hexdump -C exam4 | head   
00000000  7f 45 4c 46 01 01 01 03  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  e0 dd c3 00 34 00 00 00  |........àÝÃ.4...|
00000020  00 00 00 00 00 00 00 00  34 00 20 00 02 00 28 00  |........4. ...(.|
00000030  00 00 00 00 01 00 00 00  00 00 00 00 00 10 c0 00  |..............À.|
00000040  00 10 c0 00 e0 d5 03 00  e0 d5 03 00 05 00 00 00  |..À.àÕ..àÕ......|
00000050  00 10 00 00 01 00 00 00  94 02 00 00 94 82 0c 08  |................|
00000060  94 82 0c 08 00 00 00 00  00 00 00 00 06 00 00 00  |................|
00000070  00 10 00 00 5f ac 1b b0  55 50 58 21 ff 07 0d 0c  |...._¬.°UPX!ÿ...|
00000080  00 00 00 00 2f d2 08 00  2f d2 08 00 f4 00 00 00  |..../Ò../Ò..ô...|
00000090  80 00 00 00 08 00 00 00  77 1f a4 f9 7f 45 4c 46  |........w.¤ù.ELF|
kevin@slack:~/binaries$ strings exam4 | grep UPX
°UPX!ÿ
UPX!
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 3.07 Copyright (C) 1996-2010 the UPX Team. All Rights Reserved. $
ùUPX!u
UPX!
kevin@slack:~/binaries$
Une fois dépacké avec un coup de upx -d, le programme est quasiment celui de l'exam3.

5/ exam5 - explosion de moteur
Je n'ai pas fait cet exam, ni même tenté de le résoudre. Il fait crasher gdb, il fait planter metasm-gui, et selon le formateur seul edb debugger réussit à l'ouvrir après un temps très long. Peut-être un post de blog dans le futur :-)

Une explication de simple1 est à venir, étant un peu différent de ces keygenme.

5 commentaires:

  1. Hello,

    Pour ma part, j'arrive a ouvrir exam5 sans probleme avec metasm...
    $ ruby samples/disassemble-gui.rb --fast exam5 0x8048170 # le point d'entree
    Le desassemblage prend moins de 20 secondes sur mon PC.

    RépondreSupprimer
  2. Ca a l'air sympa comme atelier :)
    Faudra que je me plonge dans MetaASM ça me changera de HT Editor

    RépondreSupprimer
  3. @Anonyme: une fois ouvert, essayes de naviguer dedans, ça rame en boucle sans jamais finir.
    Si tu as ouvert la gui depuis un xterm, tu verras que dans celui-ci il y a des bordées successives de recherches sans fin. Ensuite, tu as peut-être un PC surpuissant :) ou c'est le switch --fast qui aide.

    @devloop: L'atelier était très bien, oui. metasm aussi d'ailleurs. Par contre, la seule doc que j'ai trouvé est contenue dans les premières lignes du programe :-/

    RépondreSupprimer
  4. Avec --fast, ca marche sans probleme (l'option desactive le moteur de backtracking, qui est gourmand en ressources). On peut naviguer fluidement, meme sur un PC modeste.

    RépondreSupprimer
  5. @Anonyme: pas concluant chez moi même avec le --fast :-(
    Je charge le binaire, touche 'c', puis espace. Ca rame quelques minutes puis ça m'affiche le graphe, jusque là tout va bien.
    Mais quoi que je fasse ensuite (list function) ou cliquer sur une valeur et appuyer sur Entrée me freeze l'interface.
    Et pourtant j'ai un dual core et 4Go de RAM.

    RépondreSupprimer