r2, pour changer de gdb

Hei, je n’avais pas n’avais pas écrit de billet ici alors que les sujets ne manquent pas…

Aujourd’hui avec des personnes de ma boîte, nous allons peut-être monter une équipe pour le NorthSec 2018 à Montréal. Il m’arrive très rarement de faire des CTF et je n’en ai pas fait depuis un moment.

Pour se préparer, on va se faire une séance de quelques heures par semaine pour faire quelques challenges. Et j’ai décidé d’apprendre à utiliser radare2 (http://radare.org/). Dans ce billet, je vais donc partager mon expérimentation du jour, ou des suivantes si j’en fais une série.

Pour symboliser cette décision, une des personnes a proposé un petit challenge, où, comme tous les CTF, il suffit de récupérer un flag. Voici comment j’ai utilisé r2:

Première résolution, simple analyse.

Le challenge n’avait rien de compliqué et prenait quelques secondes avec gdb à résoudre, je ne vais donc pas en faire un write-up, juste me concentrer sur r2.

J’ai tout d’abord tenté une approche sans exécuter le programme, mais en regardant les fonctionnalités de reverse basiques.

La première étape est de lancer r2 en lui passant le programme

1
 AmarOk@tars3  ~/Downloads  r2 -d ./easy_reverse

(le -d servira pour la seconde approche, par le debug)

Puis avec des tutoriels en ligne, je trouve les commandes aa pour lancer une analyse du programme (pour anyalyze all) puis (pdf @main pour obtenir le code qui m’intéresse). Ce qui donne :

pdfmain

Chose cool, il est possible de visualiser son code par blocs via VV @main.

VV

En regardant vite fait, on voit bien le processus du programme : 1. On demande a l’utilisateur un password

1
2
3
4
5
6
7
8
 mov edi, str.Password_:     ; 0x4007c4 ; "Password : "
 mov eax, 0
 call sym.imp.printf         ; int printf(const char *format)
 lea rax, [local_70h]
 mov rsi, rax
 mov edi, 0x4007d0
 mov eax, 0
 call sym.imp.__isoc99_scanf

via printf et scanf.

Puis on compare cette valeur avec une autre (call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2))

Et on félicite ou non la personne.

1
2
3
4
5
6
7
8
   ,=< 0x00400704      7511           jne 0x400717
   |   0x00400706      bfd3074000     mov edi, str.You_re_a_Wizard ; 0x4007d3 ; "You're a Wizard"
   |   0x0040070b      e810feffff     call sym.imp.puts           ; int puts(const char *s)
   |   0x00400710      b800000000     mov eax, 0
  ,==< 0x00400715      eb0f           jmp 0x400726
  |`-> 0x00400717      bfe3074000     mov edi, str.Try_next_year_... ; 0x4007e3 ; "Try next year ..."
  |    0x0040071c      e8fffdffff     call sym.imp.puts           ; int puts(const char *s)
  |    0x00400721      b801000000     mov eax, 1

On suppose simplement que la valeur qu’on cherche c’est le bon mot de passe et je m’intéresse donc à strcmp. En suivant les mov de dessus, on voit que strcmp compare le password qu’on à entré et une valeur que nous voyons en haut :

1
2
3
4
5
movabs rax, 0x3857397b47414c46
mov qword [local_80h], rax
mov dword [local_78h], 0x7a6e6273
mov word [local_74h], 0x7d38
mov byte [local_72h], 0

qui correspond a FLAG{9W8sbnz8} à l’envers, notre flag. Pour savoir pourquoi c’est à l’envers, je vous recommande les articles que Ge0 a pu faire sur ce site.

Seconde méthode, à l'exécution

Pour le coup, sans radare, j’aurais utilisé gdb en mettant un breakpoint et regardant le contenu de ce qui m’intéressait. Faisons donc ceci avec r2

Pour ceci, il faut lancer radare en mode debug (-d de tout à l’heure). Toutes les commandes de r2 pour le debug commencent par d, voir ici : https://radare.gitbooks.io/radare2book/content/introduction/basic_debugger_session.html. Ainsi prenons la ligne qui nous intéresse :

0x004006f2 e869feffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)

Puis, on peut lancer le programme avec dc. Une fois le breakpoint atteint, on peut alors récupérer le contenu de rdx contenant notre flag avec px @ rdx:

px

Voilà pour ma petite découverte de radare, peut-etre la suite au prochain episode.

13 commentaires

Ouai. gdb c’est cool si tu veux debug ton programme. Avoir les backtraces avec un petit mode visuel est rapide. radare2 est bien plus oriente reverse (avec des modes visuels assez sympas), tu peux commenter l’assembleur, chercher des ROP gadgets, filtrer par instructions, etc. Le fait qu’il fasse aussi debugger est bonus.

Chouette comme billet. J’ai toujours eu peur de radare2 mais il faudra bien que je m’y mette un jour puisque comme vous l’avez dit gdb est plutôt limité niveau reverse.

Et merci pour la référence AmarOk ;)

Ge0 au plaisir. Mais ouai j’ai toujours eu la flemme de m’y mettre aussi :D

Salut,

Merci pour m’avoir fait découvrir cet outil très intéressant. J’ai toutefois une question pour le coup : quid si l’exécutable a été « nettoyé » avec strip(1) ? Je ne sais pas si cela est propre à OpenBSD, mais dans un tel cas, la méthode aa puis VV ne permet pas de visualiser l’arborescence du programme.

+0 -0

Faut se débrouiller sans les symboles, disons. Dans l’exemple donné par Amazon on part du postulat de base qu’il s’agit d’un binaire génère avec Go. Avoir cette info facilité grandement la chose plutôt que de reverser "à la dure" comme si tu plongeais dans le runtime de Go sans t’en rendre compte.

Mmm… Apparemment il est possible d’afficher toutes les fonctions détectées avec f (les fonctions sont affichées sous la forme fcn.<addresse_hexa>). Techniquement, cela affiche plus que les fonctions, mais il est possible de filtrer en utilisant fs functions avant. Une fois la liste obtenue, il est possible de regarder chaque fonction avec VV @fcn.<addresse_hexa> jusqu’à trouver la bonne. ^^

Cela étant, c’est marrant, la fonction strip employée sous OpenBSD semble rendre le symbole main introuvable pour r2 alors qu’il le reste sous Linux (en tous les cas Debian)…

+1 -0

Cela étant, c’est marrant, la fonction strip employée sous OpenBSD semble rendre le symbole main introuvable pour r2 alors qu’il le reste sous Linux (en tous les cas Debian)…

Taurre

Tu es sûr de ça ? J’avais le même résultat sous (Arch)Linux.

Edit : mal lu, je recommence…

Edit 2 : Je confirme. C’est peut-être lié à une version différence de radare 2 ? Voici la mienne :

1
2
3
ge0@venom ~/c » r2 -v
radare2 2.4.0 0 @ linux-x86-64 git.2.4.0
commit: HEAD build: 2018-03-06__21:18:27
+0 -0

Mmm… J’ai fait le test sur un Ubuntu 16.04 (mea culpa, pas Debian donc) et oui, la version est ancienne (0.9.6 je crois :-° ). Sous OpenBSD 6.2, la version installée est la 10.6 (c’est mieux, mais pas encore la plus récente). Donc oui, c’est peut-être bien cela.

+0 -0

Bon, après avoir compilé la dernière version de chaque côté, la différence persiste : sous Ubuntu (16.04) le symbole main est toujours trouvable avec VV @main même après un strip -s alors qu’il ne l’est plus sous OpenBSD (6.2). Peut-être la différence vient-elle de la commande strip elle-même ?

À ce sujet, il semble qu’OpenBSD soit resté à la version 2.17 de binutils, pour des raisons de license (non GPL 3).

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte