18/07/2010

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 :

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 !