Write up RMLL crackme level 1
Ce billet aurait dû sortir il y a une semaine mais le week-end dernier se déroulait le smpCTF auquel j'ai participeéau sein de l'équipe BiG-DadDy ( arrivée 12ème ) ou avec dad nous nous sommes occupés surtout des épreuves p0wnMe, bref maintenant j'ai le temps donc je le consacre à faire ce write up.
Du 6 au 11 Juillet 2010, à Bordeaux se déroulait les Rencontres Mondiales du Logiciel Libre. Je fais actuellement un stage dans la boite XXXX et mon maître de stage se rendait sur place ( je n'ai pas eu la chance de l'accompagner :( ). Bref, dès son premier jour d'arrivée il m'envoie un e-mail avec ce lien.
Quoi des challenges de sécurité !?! Je me précipite sur les crackme.
Première approche :
w4kfu@vm-debian:~/RMLL$ ./mars0
serial : 34534343423432403245-450-435-043-50435
Nice try ! But ... no ...
Allez hop on sort gdb :
w4kfu@vm-debian:~/RMLL$ gdb ./mars0
...
(gdb) disas main
....
0x0804855b <main+9>: movl $0xc49c6547,0x1e(%esp)
0x08048563 <main+17>: movl $0xee987f5e,0x22(%esp)
0x0804856b <main+25>: movl $0x4cced336,0x26(%esp)
0x08048573 <main+33>: movl $0xee7c11e4,0x2a(%esp)
.....
0x080485a9 <main+87>: call 0x80483e4 <strlen@plt>
0x080485ae <main+92>: cmp $0x10,%eax
....
0x080485d2 <main+128>: call 0x80484f4 <chiffre>
....
0x080485ee <main+156>: call 0x80483b4 <memcmp@plt>
0x080485f3 <main+161>: test %eax,%eax
0x080485f5 <main+163>: jne 0x804860a <main+184>
....
Déjà ici on peut commencer à comprendre un peu le fonctionnement de notre crackme :
- 16 octets qui sont placés à la suite dans la stack.
- Notre serial doit être d'une longueur de 16 ( 0x10 ) caractères.
- Une fonction chiffre.
- Le test se fait à partir d'un memcmp().
Posons un breakpoint sur le memcmp() :
(gdb) b* main+156
Breakpoint 1 at 0x80485ee
(gdb) r
Starting program: /home/w4kfu/RMLL/mars0
serial : 1234567890123456
Breakpoint 1, 0x080485ee in main ()
(gdb) x/4x $esp
0xbfffecc0: 0xbfffecef 0xbfffecde 0x00000010 0x080483a0
(gdb) x/4x 0xbfffecef
0xbfffecef: 0xb5dd0032 0x85f83d00 0x3ca1842f 0xca5e21a6 <<< Notre SERIAL
(gdb) x/4x 0xbfffecde
0xbfffecde: 0xc49c6547 0xee987f5e 0x4cced336 0xee7c11e4 <<< Ce a quoi il est comparee
Regardons maitenant du côté de la fonction chiffre :
(gdb) disas chiffre
0x080484f5 <chiffre+1>: mov %esp,%ebp
0x080484f7 <chiffre+3>: sub $0x20,%esp
0x080484fa <chiffre+6>: movl $0x82e83303,-0x15(%ebp)
0x08048501 <chiffre+13>: movl $0xbac50639,-0x11(%ebp)
0x08048508 <chiffre+20>: movl $0x19abd6e,-0xd(%ebp)
0x0804850f <chiffre+27>: movl $0x8f1d6099,-0x9(%ebp)
0x08048516 <chiffre+34>: movb $0x0,-0x5(%ebp)
0x0804851a <chiffre+38>: movl $0x0,-0x4(%ebp)
0x08048521 <chiffre+45>: jmp 0x804854a <chiffre+86>
0x08048523 <chiffre+47>: mov -0x4(%ebp),%eax
0x08048526 <chiffre+50>: add 0x8(%ebp),%eax
0x08048529 <chiffre+53>: mov -0x4(%ebp),%edx
0x0804852c <chiffre+56>: add 0x8(%ebp),%edx
0x0804852f <chiffre+59>: movzbl (%edx),%ecx
0x08048532 <chiffre+62>: mov -0x4(%ebp),%edx
0x08048535 <chiffre+65>: lea (%ecx,%edx,1),%edx
0x08048538 <chiffre+68>: mov %edx,%ecx
0x0804853a <chiffre+70>: mov -0x4(%ebp),%edx
0x0804853d <chiffre+73>: movzbl -0x15(%ebp,%edx,1),%edx
0x08048542 <chiffre+78>: xor %ecx,%edx
0x08048544 <chiffre+80>: mov %dl,(%eax)
0x08048546 <chiffre+82>: addl $0x1,-0x4(%ebp)
0x0804854a <chiffre+86>: cmpl $0xf,-0x4(%ebp)
0x0804854e <chiffre+90>: jle 0x8048523 <chiffre+47>
0x08048550 <chiffre+92>: leave
0x08048551 <chiffre+93>: ret
Le disas ci-dessus peut être traduit de la sorte en C :
#include <stdio.h> char *chiffre(char *cherche, char *sec); int main(void) { int i; char *mot_chercher = "1234567890123456"; char *second_word = "\x03\x33\xe8\x82\x39\x06\xc5\xba\x6e\xbd\x9a\x01\x99\x60\x1d\x8f"; char *mot_crypt; mot_crypt = chiffre(mot_chercher,second_word); for ( i = 0 ; i < 16 ; i++) printf("%x ",mot_crypt[i]); return 0; } char * chiffre(char *cherche, char *sec) { int i = 0; static char found[16]; for ( i = 0; i < 16; i++) found[i] = (cherche[i] + i)^ sec[i]; return found; }
On teste pour voir si on retrouve bien le même résultat :
w4kfu@vm-debian:~/RMLL/mars$ gcc -o mars0 mars0.c
w4kfu@vm-debian:~/RMLL/mars$ ./mars0
32 0 dd b5 0 3d f8 85 2f 84 a1 3c a6 21 5e ca
Notre code est correct, bien maintenant il faut retrouver le serial valide, ce n'est pas très compliqué car nous savons à quoi il doit être égal. Nous n'avons qu'à faire l'opération inverse :
#include <stdio.h> char *chiffre(char *cherche, char *sec); int main(void) { char *mot_chercher = "\x47\x65\x9c\xc4\x5e\x7f\x98\xee\x36\xd3\xce\x4c\xe4\x11\x7c\xee"; char *second_word = "\x03\x33\xe8\x82\x39\x06\xc5\xba\x6e\xbd\x9a\x01\x99\x60\x1d\x8f"; printf("Le serial est %s\n", chiffre(mot_chercher,second_word)); } char * chiffre(char *cherche, char *sec) { int i = 0; static char found[16]; for ( i = 0; i < 16; i++) found[i] = (cherche[i] ^ sec[i])-i; return found; }
On exécute tout ça :
w4kfu@vm-debian:~/RMLL/mars$ gcc -o mars0 mars0.c
w4kfu@vm-debian:~/RMLL/mars$ ./mars0
Le serial est DUrCctWMPeJBqdSR
Et on teste notre serial :
w4kfu@vm-debian:~/RMLL$ ./mars0
serial : DUrCctWMPeJBqdSR
Good job !
Use your serial as password for the next level !