TinyCTF 2014 -> Speed WriteUp(rev300)

Download | Vote Up (0) | Vote Down (0)

# TinyCTF -> Speed WriteUp(rev300)
# Author : fr0g
# Twitter : https://twitter.com/fr0gSecurity / https://twitter.com/HexpressoCTF
# http://hexpresso.wordpress.com/
# Greetz to : Hask, Pastek, Soup42, Sakiir, NotFound, saxx, BitK, atmoner, ex0ns, kallimero, st0rn & Santa Claus

Je tiens à préciser que je n'expliquerai pas ici le fonctionnement de chaque fonction,
mais simplement comment résoudre l'épreuve, pour la simple et bonne raison qu'à l'heure où j'écris ce paper,
je n'ai pas encore entièrement saisi l'algo.

File : rev300: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x39fe3fa9ef192e30d8a18451110b726b4179a9a7, stripped

md5 : 0785bfc33a983c0e85eb261cfdf5a46b

-------------------------------------------------------------------------------------------

Première Execution :

$> ./rev300
Access denied

$> ./rev300 toto
Access denied

$> objdump -h rev300
...
12 .text 0000036c 08048360 08048360 00000360 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
...

Désassemblage :

gdb-peda$ x/20i 0x08048360
0x8048360: xor ebp,ebp
0x8048362: pop esi
0x8048363: mov ecx,esp
0x8048365: and esp,0xfffffff0
0x8048368: push eax
0x8048369: push esp
0x804836a: push edx
0x804836b: push 0x8048690
0x8048370: push 0x8048620
0x8048375: push ecx
0x8048376: push esi
0x8048377: push 0x80485b5 ;; main() offset
0x804837c: call 0x8048340 <__libc_start_main@plt>

Posons donc un bp sur la fonction main (0x80485b5), et lançons le programme.
Au début de la fonction, on peut voir que velui ci attend un paramètre :

=> 0x80485be: cmp DWORD PTR [ebp+0x8],0x1
0x80485c2: jle 0x80485fe ;; jump sur le message d'erreur 'Access denied' si aucun paramètre donné

if (argc <= 1){
printf("Access denied\n");
return (0);
}

On relance donc le programme avec un password en param, une fois la condition remplie,
celui ci va appeler une fonction prenant en paramètre le password entré par l'user,
ainsi qu'un entier (qui ne m'as pas servi à résoudre l'épreuve, et dont je n'ai pas encore tout à fait saisi l'utilité) :

0x80485d4: mov DWORD PTR [esp],eax ;; ici EAX => argv[1]
=> 0x80485d7: call 0x8048414

A partir de là, plus qu'à suivre le chemin :) on break sur l'appel à cette fonction 0x8048414,

Une fois dans la fonction concernée, on peut voir que l'instruction cmp sur le registre al
(avec à chaque fois une valeur différente) est assez récurrente, on va donc parcourir les instructions (gdb> ni) jusqu'à atteindre le premier cmp:

0x8048491: cmp al,0x69
0x8048493: je 0x80484e8
0x8048495: mov eax,0x0
__0x804849a: jmp 0x8048536
|
|
|> 0x8048536: leave
0x8048537: ret

--------------------------------
gdb-peda$ set $al=0x69
gdb-peda$ continue

A ce moment là, le registre al contient la valeur du premier caractère du password entré par l'user,
et nous pouvons voir que le premier caractère du password attendu est 0x69 ('i'),
on set al à 'i', afin que le saut conditionnel suivant la comparaison soit pris, et
on continue jusqu'à tomber sur le rappel de la fonction (manifestement récursive), qui vas
comparer le caractère suivant.
Si les deux valeurs sont différentes lors d'une comparaison de caractère, un saut absolu sera pris plus bas afin de quitter la fonction courante avec un return 0;

il ne reste plus qu'à répéter cette operation pour les autres caractères,
aucune autre verification n'est susceptible d'intérrompre la procédure que
les sauts conditionnels après chaque "cmp al,0x??", un nuage de criquets, ou un THE GAME imprévu :þ.
Une fois tous les caractères passés, et le mot de passe attendu "isengard" découvert, la fonction courante fini par un return 1,
qui nous renvoie dans le main.
S'en suit l'appel de la fonction qui génère et affiche le flag (prenant en param le mot de passe entré),
que je n'ai pas encore analysée, disponible en *bas pour les intéressés

On exécute le programme avec le bon mot de passe en paramètre, et c'est tout bon :)

(2:716)$ ./rev300 isengard
Access granted
flag{s0me7hing_S0me7hinG_t0lki3n}

-------------------------------------------------------------------------------------------

*Fonction générant le flag :

0x8048538: push ebp
0x8048539: mov ebp,esp
0x804853b: push edi
0x804853c: push esi
0x804853d: push ebx
0x804853e: sub esp,0xac
0x8048544: lea edx,[ebp-0xa0]
0x804854a: mov ebx,0x8048760
0x804854f: mov eax,0x21
0x8048554: mov edi,edx
0x8048556: mov esi,ebx
0x8048558: mov ecx,eax
0x804855a: rep movs DWORD PTR es:[edi],DWORD PTR ds:[esi]
0x804855c: mov DWORD PTR [ebp-0x1c],0x0
0x8048563: jmp 0x8048598
0x8048565: mov eax,DWORD PTR [ebp-0x1c]
0x8048568: mov ecx,DWORD PTR [ebp+eax*4-0xa0]
0x804856f: mov eax,DWORD PTR [ebp-0x1c]
0x8048572: mov edx,eax
0x8048574: sar edx,0x1f
0x8048577: shr edx,0x1d
0x804857a: add eax,edx
0x804857c: and eax,0x7
0x804857f: sub eax,edx
0x8048581: add eax,DWORD PTR [ebp+0x8]
0x8048584: movzx eax,BYTE PTR [eax]
0x8048587: movsx eax,al
0x804858a: xor eax,ecx
0x804858c: mov DWORD PTR [esp],eax
0x804858f: call 0x8048350 <putchar@plt>
0x8048594: add DWORD PTR [ebp-0x1c],0x1
0x8048598: cmp DWORD PTR [ebp-0x1c],0x20
0x804859c: jle 0x8048565
0x804859e: mov DWORD PTR [esp],0xa
0x80485a5: call 0x8048350 <putchar@plt>
0x80485aa: add esp,0xac
0x80485b0: pop ebx
0x80485b1: pop esi
0x80485b2: pop edi
0x80485b3: pop ebp
0x80485b4: ret

fr0g


Comments

saelyx
Si on dessasemble le programme en entier, on a pas la liste des cmp al, 0x?? et ainsi plus qu'à recomper le password qui valide ?

Please login to comment !