ELF - Random Crackme
mardi 9 avril 2013 à 15:10Le challenge consiste à donner le bon mot de passe. Le fichier ch5.zip est fourni.
Décompresser l'archive... Facile !
On se retrouve avec un fichier nommé crackme_wtf.
MàJ : il suffit de faire un "ar -x crackme_wtf" pour décompresser le fichier.
Tu peux sauter cette étape.
Sous Debian GNU/Linux, ce fichier a l'icône d'une archive, cependant, pas moyen d'en faire quelques chose.
La commande
Nous avons enfin l'exécutable, voyons ce qu'on peut en faire.
Je tente quand même d'ouvrir le binaire dans un débugger :
Et bien il y a une variable d'environnement sous le doux nom de LD_PRELOAD qui sert à charger ses propres bibliothèques de fonction. Je vais m'en servir pour dire au programme de prendre la mienne plutôt que celle du système.
Voici le code pour hook_strcmp.c :
Pour trouver le prototype d'une fonction, va faire un tour sur ce site.
Nous avons donc notre bibliothèque perso, voyons ce que ça donne :
Testons str2 :
Tout à l'heure, la commande strings nous a renvoyé d'autres noms de fonctions utilses pour le pseudo-aléatoire tel que
C'est parti pour de l'usurpation de bourin, hook_multi.c :
Testons :
Les usurpations de
Résultat :
Je réessaie 3-4 fois et str2 vaut toujours la même chose, je l'entre comme mot de passe et.. TADAAA !!
Je me suis inspiré de cette source.
Étape 1
Décompresser l'archive... Facile !
On se retrouve avec un fichier nommé crackme_wtf.
Étape 2
MàJ : il suffit de faire un "ar -x crackme_wtf" pour décompresser le fichier.
Tu peux sauter cette étape.
Sous Debian GNU/Linux, ce fichier a l'icône d'une archive, cependant, pas moyen d'en faire quelques chose.
La commande
file
renvoie :$ file crackme_wtf
crackme_wtf: current ar archive
Bon, voyons à l'aide d'un éditeur héxadécimal ce que donnent les 8 premiers octets :$ hexdump -n 8 -C crackme_wtf
00000000 21 3c 61 72 63 68 3e 0a |!<arch>.|
Mouai... À tout hasard, l'excellent hachoir-subfile saura peut-être m'en dire plus.(hachoir-subfile) L'idée est d'extraire des fichiers (non compressés, non chiffrés et non fragmentés) contenus dans d'autres fichier Victor STINNER.
$ hachoir-subfile crackme_wtf
[+] Start search on 14418 bytes (14.1 KB)
[+] File at 0: Unix archive
[+] File at 424: ELF Unix/BSD program/library: 32 bits
[+] End of search -- offset=14418 (14.1 KB)
Hop hop ! Que vois-je ? À l'offset 424 il y aurait un binaire ELF ? Ok, tail
est notre ami :$ tail --bytes=+425 crackme_wtf >Crackme
$ file Crackme
chal: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.8, not stripped
$ ldd Crackme
linux-gate.so.1 => (0xf7736000)
libc.so.6 => /lib32/libc.so.6 (0xf75b3000)
/lib/ld-linux.so.2 (0xf7737000)
$ chmod +x Crackme
$ ./Crackme
** Bienvenue dans ce challenge de cracking **
[+] Password :azerty
[!]Access Denied !
[-] Try again
L'exécutable est lié dynamiquement aux bibliothèques, ce qui sera intéressant par la suite.Étape 3
Nous avons enfin l'exécutable, voyons ce qu'on peut en faire.
$ strings Crackme
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
exit
sprintf
srand
puts
time
__stack_chk_fail
stdin
getpid
fgets
strlen
memset
getchar
__errno_location
fputc
malloc
stderr
ptrace
fwrite
strchr
strcmp
__libc_start_main
free
GLIBC_2.4
GLIBC_2.0
PTRh
VTEP
IQAV
Y[_]
[^_]
_VQLGE_TQPTYD_KJTIV_
%lld%s
[-] Allocation memoire echouee!
[-]Debugger detecte ... Exit
** Bienvenue dans ce challenge de cracking **
[+] Password :
[!]Access Denied !
[-] Try again
[+]Good password
[+] Clee de validation du crack-me :
%d_%s%d_
Il y a une protection anti-debugger apparemment et quelques appels à des fonctions qui peuvent être intéressantes telles que strcmp
, strlen
ou même strchr
.Je tente quand même d'ouvrir le binaire dans un débugger :
$ gdb Crackme
# (...)
Reading symbols from /home/tiger-222/chal...done.
(gdb) run
Starting program: /home/tiger-222/chal
[-]Debugger detecte ... Exit
[Inferior 1 (process 6523) exited with code 01]
(gdb) quit
$ strace ./Crackme
(...)
write(2, "[-]Debugger detecte ... Exit\n", 29[-]Debugger detecte ... Exit
(...)
Comme il fallait s'y attendre, gdb
et strace
ne fonctionnent pas. J'opte pour l'usurpation (hook) de la fonction strcmp
.Étape 4
Alors, l'usurpation, comment ça se passe ?Et bien il y a une variable d'environnement sous le doux nom de LD_PRELOAD qui sert à charger ses propres bibliothèques de fonction. Je vais m'en servir pour dire au programme de prendre la mienne plutôt que celle du système.
Voici le code pour hook_strcmp.c :
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
int strcmp(const char *str1, const char *str2) {
printf("[Hook strcmp] str1 = %s\n", str1);
printf(" str2 = %s\n", str2);
// Appel de la véritable fonction
typeof(strcmp)* function;
function = dlsym(RTLD_NEXT, "strcmp");
return (*function)(str1, str2);
}
Que l'on compile tel que :$ gcc -Wall -fPIC -c -o hook_strcmp.o hook_strcmp.c
hook_strcmp.c:5:5: warning: conflicting types for built-in function ‘strcmp’ [enabled by default]
$ gcc -shared -fPIC -Wl,-soname -Wl,hook_strcmp.so -ldl -o /tmp/hook_strcmp.so hook_strcmp.o
L'erreur, c'est normal, le compilo nous informe officiellement que notre fonction est en conflit avec celle du système, tiens donc !Pour trouver le prototype d'une fonction, va faire un tour sur ce site.
Nous avons donc notre bibliothèque perso, voyons ce que ça donne :
$ export LD_PRELOAD="/tmp/hook_strcmp.so"
$ ./Crackme
** Bienvenue dans ce challenge de cracking **
[+] Password :azerty
[Hook strcmp] str1 = azerty
str2 = 235625580_VQLGE_TQPTYD_KJTIV_
[!]Access Denied !
[-] Try again
Ah ! C'est un début prometteur !Testons str2 :
$ ./Crackme
** Bienvenue dans ce challenge de cracking **
[+] Password :235625580_VQLGE_TQPTYD_KJTIV_
[Hook strcmp] str1 = 235625580_VQLGE_TQPTYD_KJTIV_
str2 = 139734408_VQLGE_TQPTYD_KJTIV_
[!]Access Denied !
[-] Try again
Maintenant c'est clair, le mot de passe est constitué d'un nombre qui semble aléatoire suivi de la chaîne _VQLGE_TQPTYD_KJTIV_.Tout à l'heure, la commande strings nous a renvoyé d'autres noms de fonctions utilses pour le pseudo-aléatoire tel que
srand
, rand
et time
. Notons aussi getpid()
, il arrive que le pid soit ajouté comme sel.C'est parti pour de l'usurpation de bourin, hook_multi.c :
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
int strcmp(const char *str1, const char *str2) {
printf("[Hook strcmp] str1 = %s\n", str1);
printf(" str2 = %s\n", str2);
// Appel de la véritable fonction
typeof(strcmp)* function;
function = dlsym(RTLD_NEXT, "strcmp");
return (*function)(str1, str2);
}
int getpid() { printf("[Hook getpid] 0\n"); return 0; }
int srand() { printf("[Hook srand] 0\n"); return 0; }
int time() { printf("[Hook time] 0\n"); return 0; }
Tout le monde renverra 0, comme ça pas de chichi, si le programme souhaite un nombre pseudo-aléatoire entre 0 et 0, ça restreint les possibilités !Testons :
$ gcc -Wall -fPIC -c -o hook_multi.o hook_multi.c
hook_strcmp.c:5:5: warning: conflicting types for built-in function ‘strcmp’ [enabled by default]
$ gcc -shared -fPIC -Wl,-soname -Wl,hook_multi.so -ldl -o /tmp/hook_multi.so hook_multi.o
$ export LD_PRELOAD="/tmp/hook_multi.so"
$ ./Crackme
[Hook time] 0
[Hook srand] 0
[Hook getpid] 0
[Hook srand] 0
[-]Debugger detecte ... Exit
Arf ! Pas bon ça !Les usurpations de
time
, srand
et getpid
semblent fonctionner, je supprime celle de rand
... Ça fonctionne. Vilain petit canard !Résultat :
$ export LD_PRELOAD="/tmp/hook_multi.so"
$ ./Crackme
[Hook time] 0
[Hook srand] 0
[Hook getpid] 0
** Bienvenue dans ce challenge de cracking **
[+] Password :azerty
[Hook strcmp] str1 = azerty
str2 = 0_VQLGE_TQPTYD_KJTIV_
[!]Access Denied !
[-] Try again
Je réessaie 3-4 fois et str2 vaut toujours la même chose, je l'entre comme mot de passe et.. TADAAA !!
$ ./Crackme
[Hook time] 0
[Hook srand] 0
[Hook getpid] 0
** Bienvenue dans ce challenge de cracking **
[+] Password :0_VQLGE_TQPTYD_KJTIV_
[Hook strcmp] str1 = 0_VQLGE_TQPTYD_KJTIV_
str2 = 0_VQLGE_TQPTYD_KJTIV_
[+]Good password
[+] Clee de validation du crack-me : *****
Bon je cache quand même le mot de passe pour ne pas te mâcher tout le travail.Je me suis inspiré de cette source.