Un peu de hack…

Pour ne pas (trop) perdre la main à force de ne faire que des maths, j’ai commencé récemment à jouer sur smashthestack.org, plus précisément sur io. Je vais poster ici ce que je trouve ; évidemment les pros trouveront ça trivial (au moins pour les premiers niveaux, qui n’utilisent que des techniques de base), mais les autres apprendront peut-être quelques petites choses… Pour les intéressés, ça se passe par là :

ssh level1@io.netgarage.org

Le mot de passe est level1. Le niveau 1 est un bête programme qui prend un mot de passe en paramètre :

level1@io:~$ /levels/level01 
You need to supply a password.
Usage: /levels/level01 [password]
level1@io:~$ /levels/level01 foo
Fail.

Le but est évidemment de trouver le mot de passe. La solution après le jump…

La première chose à faire est de désassembler pour avoir une idée de ce qui se passe. Voilà à quoi ressemble la fonction main() :

level1@io:~$ objdump -M intel -d /levels/level01 | nl
[...]
   191	08048596 <main>:
   192	 8048596:	55                   	push   ebp
   193	 8048597:	89 e5                	mov    ebp,esp
   194	 8048599:	83 ec 18             	sub    esp,0x18
   195	 804859c:	83 e4 f0             	and    esp,0xfffffff0
   196	 804859f:	b8 00 00 00 00       	mov    eax,0x0
   197	 80485a4:	29 c4                	sub    esp,eax
   198	 80485a6:	83 7d 08 02          	cmp    DWORD PTR [ebp+0x8],0x2
   199	 80485aa:	74 1e                	je     80485ca <main+0x34>
   200	 80485ac:	8b 45 0c             	mov    eax,DWORD PTR [ebp+0xc]
   201	 80485af:	8b 00                	mov    eax,DWORD PTR [eax]
   202	 80485b1:	89 44 24 04          	mov    DWORD PTR [esp+0x4],eax
   203	 80485b5:	c7 04 24 60 87 04 08 	mov    DWORD PTR [esp],0x8048760
   204	 80485bc:	e8 f7 fd ff ff       	call   80483b8 <printf@plt>
   205	 80485c1:	c7 45 fc 00 00 00 00 	mov    DWORD PTR [ebp-0x4],0x0
   206	 80485c8:	eb 4e                	jmp    8048618 <main+0x82>
   207	 80485ca:	e8 5e ff ff ff       	call   804852d <pass>
   208	 80485cf:	c7 44 24 08 64 00 00 	mov    DWORD PTR [esp+0x8],0x64
   209	 80485d6:	00 
   210	 80485d7:	8b 45 0c             	mov    eax,DWORD PTR [ebp+0xc]
   211	 80485da:	83 c0 04             	add    eax,0x4
   212	 80485dd:	8b 00                	mov    eax,DWORD PTR [eax]
   213	 80485df:	89 44 24 04          	mov    DWORD PTR [esp+0x4],eax
   214	 80485e3:	c7 04 24 a0 91 04 08 	mov    DWORD PTR [esp],0x80491a0
   215	 80485ea:	e8 b9 fd ff ff       	call   80483a8 <mbstowcs@plt>
   216	 80485ef:	c7 44 24 04 40 91 04 	mov    DWORD PTR [esp+0x4],0x8049140
   217	 80485f6:	08 
   218	 80485f7:	c7 04 24 a0 91 04 08 	mov    DWORD PTR [esp],0x80491a0
   219	 80485fe:	e8 d5 fd ff ff       	call   80483d8 <wcscmp@plt>
   220	 8048603:	85 c0                	test   eax,eax
   221	 8048605:	75 05                	jne    804860c <main+0x76>
   222	 8048607:	e8 a8 fe ff ff       	call   80484b4 <win>
   223	 804860c:	c7 04 24 95 87 04 08 	mov    DWORD PTR [esp],0x8048795
   224	 8048613:	e8 d0 fd ff ff       	call   80483e8 <puts@plt>
   225	 8048618:	8b 45 fc             	mov    eax,DWORD PTR [ebp-0x4]
   226	 804861b:	c9                   	leave  
   227	 804861c:	c3                   	ret    
   228	 804861d:	90                   	nop
   229	 804861e:	90                   	nop
   230	 804861f:	90                   	nop
[...]

En regardant les lignes 219 et suivantes, on voit que le code fait un wcscmp(), et appelle la fonction win() si et seulement si la comparaison retourne 0 (c’est-à-dire si les deux chaînes comparées sont identiques). On note également que la fonction wcscmp() compare des chaînes de caractères Unicode (ou autre encodage exotique), et pas ASCII (je suppute que c’est pour qu’on ne puisse pas tout bêtement utiliser l’outil strings), cela sera important par la suite.

L’appel à wcscmp() compare les chaînes stockées aux adresses 0x8049140 et 0x80491a0. Il serait sans doute intéressant d’aller voir ce qui s’y trouve…

level1@io:~$ gdb -q --args /levels/level01 foo
Reading symbols from /levels/level01...(no debugging symbols found)...done.
(gdb) break *0x80485fe
Breakpoint 1 at 0x80485fe
(gdb) start
Temporary breakpoint 2 at 0x804859c
Starting program: /levels/level01 foo

Temporary breakpoint 2, 0x0804859c in main ()
(gdb) cont
Continuing.

Breakpoint 1, 0x080485fe in main ()
(gdb) x/10bc 0x8049140
0x8049140 <pw>:	83 'S'	0 '\000'	0 '\000'	0 '\000'	101 'e'	0 '\000'	0 '\000'	0 '\000'
0x8049148 <pw+8>:	99 'c'	0 '\000'
(gdb) x/10bc 0x80491a0
0x80491a0 <input>:	102 'f'	0 '\000'	0 '\000'	0 '\000'	111 'o'	0 '\000'	0 '\000'	0 '\000'
0x80491a8 <input+8>:	111 'o'	0 '\000'
(gdb) 

On voit notre chaîne “foo” à 0x80491a0 (avec des octets à 0 intercalés par la conversion en Unicode), donc il y a fort à parier que l’autre chaîne est notre mot de passe…

(gdb) x/50bc 0x8049140
0x8049140 <pw>:	83 'S'	0 '\000'	0 '\000'	0 '\000'	101 'e'	0 '\000'	0 '\000'	0 '\000'
0x8049148 <pw+8>:	99 'c'	0 '\000'	0 '\000'	0 '\000'	114 'r'	0 '\000'	0 '\000'	0 '\000'
0x8049150 <pw+16>:	101 'e'	0 '\000'	0 '\000'	0 '\000'	116 't'	0 '\000'	0 '\000'	0 '\000'
0x8049158 <pw+24>:	80 'P'	0 '\000'	0 '\000'	0 '\000'	87 'W'	0 '\000'	0 '\000'	0 '\000'
0x8049160 <pw+32>:	0 '\000'	0 '\000'	0 '\000'	0 '\000'	10 '\n'	0 '\000'	10 '\n'0 '\000'
0x8049168 <pw2+4>:	83 'S'	0 '\000'	101 'e'	0 '\000'	99 'c'	0 '\000'	114 'r'	0 '\000'
0x8049170 <pw2+12>:	101 'e'	0 '\000'

En gardant un octet sur quatre, on voit que la chaîne est “SecretPW”, donc allons-y :

level1@io:~$ /levels/level01 SecretPW
Win!

You will find the ssh password for level2 in /home/level2/.pass
sh-4.1$ 

Et hop. :)

3 thoughts on “Un peu de hack…

  1. Karl

    Haha, t’es allé chercher loin.

    Perso, j’ai fait cat level01 et il m’a affiché SecretPW avant de faire des bêtises. Ça puait le password obvious de level 02, j’ai testé, j’ai gagné. :D

    Sinon, merci pour le lien, je vais jouer aussi.

    Reply
  2. Karl

    Sinon, de manière plus propre, “strings -td -el level01” affiche correctement le mot de passe sans se prendre la tête, des fois qu’on soupçonne la présence de chaînes de caractères en Unicode, comme tu l’as fait en observant la présence de wcscmp().

    (ouais, double-post, je te laisse la liberté de l’ajouter à mon message précédent :p)

    Reply
    1. Firas Post author

      Le but des premiers niveaux est surtout de se familiariser avec certaines techniques de base (ici, objdump/gdb). cat et strings ne seront d’aucun secours sur des exemples un peu plus sophistiqués, donc les utiliser ici n’a pas grand intérêt formatif.

Leave a Reply

Your email address will not be published. Required fields are marked *