

FIXME : ici ton texte d'introduction, emp ? sinon j'en fais un



Aaaah c'tait une bonne surprise qu'emp remette le couvert avec un crackme3 !
Je me suis mis dessus aussitt que je l'ai vu, et tant donn que je l'ai fini
en premier (aucune vantardise ici, je suis certain que si saurtains avaient eu
le temps de se pencher dessus ils l'auraient crack plus vite et mieux),
je vous donne ici la faon dont j'ai procd.



  premiers bats avec la bte
  ***************************

$ cp crk3 crk3.org # un ptit backup fait jamais de mal

On sait que le binaire est au format ELF, on va donc sortir readelf en esperant
recuperer les infos utiles :

$ readelf -a crk3
[...]
  Adresse du point d'entre:         0x804828a
[...]
En-ttes de programme:
  Type           Dcalage Adr. vir.  Adr.phys.  T.Fich. T.Mm.  Fan Alignement
  LOAD           0x000000 0x08048000 0x08048000 0x0038c 0x0038c RWE 0xa5001000
[...]

Voila tout ce qui m'intressait :
- l'excutable est form d'un seul segment mmoire, qui sera mapp a partir de
l'adresse virtuelle 0x08048000, c'est--dire que, vu depuis le processus crk3,
on trouvera le premier octet du binaire crk3  l'adresse 0x08048000, et tout le
contenu de ce binaire se trouvera transpos dans cette zone unique.
A noter que les droits sur ce segment sont RWX, puisqu'il faut au moins pouvoir
lire et executer les opcodes, et ecrire le password en mmoire lorsqu'il sera
fourni par le luser durant l'excution.
Cela prsage aussi du decryptage de code en direct, comme dans le crkme2,
puisque le code est situ dans un segment writable (ce qui n'est pas le cas
des programmes faits de faon conventionnelle, "cat /proc/self/maps" pour
celles et ceux qui auraient un doute).

- l'adresse du point d'entre est 0x804828a, et tant donn les remarques
prcdentes, le 1er opcode excut sera le 0x28a me du fichier crk3.

- comme la dernire fois, emp n'a pas laiss la tables des symboles, ni aucune
information sur les sections, ni sur rien d'autre en fait ;)



  dsassemblage
  *************

tout d'abord, quelques petites options intressantes de ndisasm
-b bits
 se placer dans une architecture 16 bits ou 32 bits.
-o origin
 permet d'indiquer l'adresse o est plac le dbut du code  dsassembler,
a permet d'avoir l'illusion de manipuler les adresses virtuelles et donc de 
mieux s'y retrouver dans la mmoire du processus.
-s sync-point
 force le dsassemblage  se synchroniser pour ne pas "passer au-dessus"
de l'adresse indique. Ca permet de bypass le false disassembly "statique"
(qui est prsent de base dans le binaire)

Cette option de synchronisation se rvle utile ds le premier coup,
car un "ndisasm -o 0x8048000 -b 32 crk3" se fait berner :
[...]
08048287  E8EB9AEBA8        call 0xb0f01d77
0804828C  0000              add [eax],al
[...]
puisqu'il ne commence pas de lecture d'instruction  l'entry point
(adresse 0x804828a),  cause de l'octet parasite E8 en 0x08048287.


On 
$ ndisasm -o 0x8048000 -b 32 crk3 -s 0x804828a 
[...]
0804828A  EBA8              jmp short 0x8048234
[...]

Voil qui est mieux, rendez-vous en 0x8048234 :
[...]
08048234  BF52820408        mov edi,0x8048252
08048239  66B8EB01          mov ax,0x1eb
0804823D  66AB              stosw
0804823F  BF67820408        mov edi,0x8048267
08048244  66AB              stosw
08048246  BF85820408        mov edi,0x8048285
0804824B  66AB              stosw
0804824D  BF24820408        mov edi,0x8048224
08048252  90                nop
08048253  90                nop
[...]


Ouh que c'est sournois ! Le code  suivre est crit  l'excution, mais pire,
ce sont des jumps d'1 octet qui sont ainsi crits, qui dcaleront le 
dsassembleur, ce dernier nous montrera alors des instructions incohrentes
puisqu'il interprtera depuis le milieu d'une vritable instruction
(emp appelle a du live CCA : Code Changeant d'Aspect en direct).

En dtails, la 1re instruction mets la valeur 0x8048252 dans le registre edi,
cette valeur correspondant  une adresse plus loin o de petits nops 
(opcode 0x90, ne ralise aucune opration) ne demandent qu' tre crass.
Puis 0x01eb (instruction de jmp d'1 octet) est plac dans le registre ax 
(16 bits celui-ci), et le stosw va placer le mot contenu dans ax  l'adresse 
pointe par edi.
Ainsi, on n'a plus :
08048252  90                nop
08048253  90                nop
mais
08048252  EB01              jmp short 0x08048255

Cela est fait pour 3 adresses : 0x8048252, 0x8048267 et 0x8048285.
Pour y voir plus clair, j'ai utilis bvi (comme vi, mais dite en hexadcimal)
pour crire les EB 01 (et non pas 01 EB, crk3 est fait pour x86, donc criture
en mmoire selon little endian) dans le fichier crk3, qui s'appellera aprs ces
modifs crk3.1, et se remettre  dsassembler la suite qui est en 0x8048255.


$ ndisasm -o 0x8048000 -b 32 -s 0x8048255 crk3.1

08048255  B8BE548004        mov eax,0x48054be
0804825A  AB                stosd
0804825B  B80889F7B9        mov eax,0xb9f78908
08048260  AB                stosd
08048261  B8D0010000        mov eax,0x1d0
08048266  AB                stosd
08048267  EB01              jmp short 0x804826a

Pas la peine de r-expliquer, encore un ptit coup de bvi (je me rpte, mais
attention au little endian, et  son tomahawk aussi) qui met au monde crk3.2,
puis on retourne (mais sans courir!) au dsassemblage : 0x804826a.


$ ndisasm -o 0x8048000 -b 32 -s 0x804826A crk3.2

0804826A  B831DBB3A5        mov eax,0xa5b3db31
0804826F  AB                stosd
08048270  B8AC30D8AA        mov eax,0xaad830ac
08048275  AB                stosd
08048276  B8E2FAE915        mov eax,0x15e9fae2
0804827B  AB                stosd
0804827C  66B8FEFF          mov ax,0xfffe
08048280  66AB              stosw
08048282  B0FF              mov al,0xff
08048284  AA                stosb
08048285  EB01              jmp short 0x8048288

Change pas d'main, je sens qu'a vient !


$ ndisasm -o 0x8048000 -b 32 -s 0x8048288 crk3.3

08048288  EB9A              jmp short 0x8048224
A noter que ce jump emmne  une adresse o du code avait t crit en live.


$ ndisasm -o 0x8048000 -b 32 -s 0x8048224 crk3.3

08048224  BE54800408        mov esi,0x8048054
08048229  89F7              mov edi,esi
0804822B  B9D0010000        mov ecx,0x1d0
08048230  31DB              xor ebx,ebx
08048232  B3A5              mov bl,0xa5
08048234  AC                lodsb
08048235  30D8              xor al,bl
08048237  AA                stosb
08048238  E2FA              loop 0x8048234
0804823A  E915FEFFFF        jmp 0x8048054

On retrouve ici un ptit algo dcryptant le code qui dmarre en 0x8048054 et
d'une longueur de 0x1d0 octets, en l'xor'izant avec une cl "prive" : 0xa5.

Pour continuer de dsassembler, j'ai crit crk3_decoder.c qui dcode tout a
dans le fichier crk3_d.
On arrive maintenant au boss final !

$ ndisasm -o 0x8048000 -b 32 -s 0x8048054 crk3_d

08048054  B96F810408        mov ecx,0x804816f	; ecx <- 0x804816f
08048059  BA8A000000        mov edx,0x8a	; edx <- 0x8a
0804805E  E8F8000000        call 0x804815b	; 1er call - affichage texte
08048063  B91A820408        mov ecx,0x804821a	; ecx <- 0x804821a
08048068  31D2              xor edx,edx		; edx <- 0
0804806A  B208              mov dl,0x8		; dl <- 0x8, pour lire 8 char
0804806C  E8F4000000        call 0x8048165	; 2e call - lecture du pass
08048071  48                dec eax		; eax--
08048072  85C0              test eax,eax	; maj des flags selon eax
08048074  744A              jz 0x80480c0	; jmp si eax == 0 (juste lu \n)
08048076  89C6              mov esi,eax		; esi <- eax
08048078  BF1A820408        mov edi,0x804821a	; edi <- 0x804821a (adr passwd)
0804807D  E845000000        call 0x80480c7	; 3e call - KSA
08048082  BF11000000        mov edi,0x11	; edi <- 0x11
08048087  BEAF800408        mov esi,0x80480af	; esi <- 0x80480af
0804808C  E884000000        call 0x8048115	; 4e call - PRGA
08048091  BEAF800408        mov esi,0x80480af	; esi <- 0x80480af
08048096  AD                lodsd		; EAX <- [ESI]; ESI += 4
08048097  3D9090B90B        cmp eax,0xbb99090	; jmp si eax == 0xbb99090
0804809C  7411              jz 0x80480af	; jmp <=> crack0red
0804809E  B9F9810408        mov ecx,0x80481f9	; ecx <- 0x80481f9
080480A3  BA12000000        mov edx,0x12	; edx <- 0x12
080480A8  E8AE000000        call 0x804815b	; 1er call (wrong password)
080480AD  EB11              jmp short 0x80480c0 ; exit

1er call :
0804815B  31C0              xor eax,eax
0804815D  B004              mov al,0x4
0804815F  31DB              xor ebx,ebx
08048161  43                inc ebx
08048162  CD80              int 0x80 ; sys_write sur stdout du msg d'accueil
08048164  C3                ret

2e call :
08048165  31C0              xor eax,eax
08048167  B003              mov al,0x3
08048169  31DB              xor ebx,ebx ; lecture sur le descr. 1, ce qui emp_
0804816B  43                inc ebx	; eche de filer le passe en stdin
0804816C  CD80              int 0x80 ; sys_read du pass, qui est mis en 0x804821a
0804816E  C3                ret

3e call (KSA, Key Setup Algorithm) :
080480C7  B8FCFDFEFF        mov eax,0xfffefdfc
080480CC  B940000000        mov ecx,0x40
080480D1  89048D88820408    mov [ecx*4+0x8048288],eax
080480D8  2D04040404        sub eax,0x4040404
080480DD  49                dec ecx
080480DE  75F1              jnz 0x80480d1	; init tableau 804828C->8048388
080480E0  31C0              xor eax,eax
080480E2  31DB              xor ebx,ebx		; bl : index pour le pass
080480E4  BE04000000        mov esi,0x4		; esi : gre la boucle annexe
080480E9  E905000000        jmp 0x80480f3
080480EE  FEC3              inc bl
080480F0  4E                dec esi
080480F1  74EF              jz 0x80480e2	; le jmp de la boucle annexe
080480F3  8A918C820408      mov dl,[ecx+0x804828c]
080480F9  02041F            add al,[edi+ebx]
080480FC  00D0              add al,dl
080480FE  8AB08C820408      mov dh,[eax+0x804828c]
08048104  88B18C820408      mov [ecx+0x804828c],dh
0804810A  88908C820408      mov [eax+0x804828c],dl
08048110  FEC1              inc cl
08048112  75DA              jnz 0x80480ee	; melange des valeurs
08048114  C3                ret

4e call (PRGA, Pseudo-Random Generation Algorithm) :
08048115  85FF              test edi,edi
08048117  7441              jz 0x804815a
08048119  31C0              xor eax,eax
0804811B  31DB              xor ebx,ebx
0804811D  31C9              xor ecx,ecx
0804811F  31D2              xor edx,edx
08048121  FEC3              inc bl
08048123  8A938C820408      mov dl,[ebx+0x804828c]
08048129  00D0              add al,dl
0804812B  8A888C820408      mov cl,[eax+0x804828c]
08048131  888B8C820408      mov [ebx+0x804828c],cl
08048137  88908C820408      mov [eax+0x804828c],dl
0804813D  00D1              add cl,dl
0804813F  8A898C820408      mov cl,[ecx+0x804828c]
08048145  300E              xor [esi],cl
08048147  46                inc esi
08048148  4F                dec edi
08048149  75D6              jnz 0x8048121	; chiffre de [esi]  [esi+16]
0804814B  31C0              xor eax,eax
0804814D  BF8C820408        mov edi,0x804828c
08048152  B940000000        mov ecx,0x40
08048157  FC                cld
08048158  F3AB              rep stosd		; remplit 804828C->8048388 de 0
0804815A  C3                ret			; pour cacher a aux ptracers

un jump (exit) :
080480C0  31C0              xor eax,eax
080480C2  40                inc eax
080480C3  31DB              xor ebx,ebx
080480C5  CD80              int 0x80	; sys_exit

un autre jump (code excut si cracked) :
080480AF  54                push esp
080480B0  62                db 0x62
080480B1  F33064913B        rep xor [ecx+edx*4+0x3b],ah
080480B6  5A                pop edx
080480B7  1F                pop ds
080480B8  47                inc edi
080480B9  A9E109021D        test eax,0x1d0209e1
080480BE  54                push esp
080480BF  91                xchg eax,ecx
puis c'est le code de "un jump" c'est-a-dire exit



Le premier bout de programme va afficher le texte puis demander le pass, grce
 un sys_read de 8 caractres. Ce pass est stock en 0x804821a, puis vrifi
qu'il n'est pas vide. Si c'est le cas, le programme saute en 0x80480c0, qui
effectue l'appel systme sys_exit.

La vrification du pass est ici base sur le chiffrement RC4. Cet algorithme
permet de passer d'une cl (symtrique)  un nombre pseudo-alatoire.
Ca se passe en 2 parties : KSA et PRGA.

. KSA (Key Setup Algorithm)
Un tableau de 256 octets est cr, et rempli avec les valeurs de 0  255,
c'est--dire une boucle de i de 0  255 avec tableau[i] = i;
Ici, emp a choisi de remplir le tableau 4 par 4, avec la valeur 0xfffefdfc
qui est dcrmente de 0x4040404  chaque boucle.
Etonnamment, le tableau va de 0x804828c  0x8048388, ce qui ne fait que 253 octets.
FIXME : je fais une erreur de calcul ou quoi ? surtout qu'a la fin du fichier, y a bien 256 octets.

Le tableau est ensuite mlang de la faon suivante :
j = 0;
for i de 0  255
    j = (j + tableau[i] + pass[i % lg_pass]) mod 256 // lg_pass == 4 ici
    permute (S[i],S[j])

Pour raliser le modulo dans le code du 3e call, il y a une boucle annexe base
sur esi qui permet de faire tourner bl entre 0 et 3 (bl est utilis pour
prendre les octets 0  3 du pass : [edi+ebx], edi contenant l'adresse o a t
stock le pass).

A la sortie de l'initialisation du tableau (0x080480E0), ecx tait  0.
cl est incrment  chaque loop donc au bout de 256 tours la boucle s'arrte, 
 cause du jnz : 08048112  75DA              jnz 0x80480ee.

La modification de j et la permutation sont faites ici :
080480F3  8A918C820408      mov dl,[ecx+0x804828c]
080480F9  02041F            add al,[edi+ebx]		; al remplit le
080480FC  00D0              add al,dl			; role de j
080480FE  8AB08C820408      mov dh,[eax+0x804828c]
08048104  88B18C820408      mov [ecx+0x804828c],dh	; tation
0804810A  88908C820408      mov [eax+0x804828c],dl	;       permu


. PRGA (Pseudo-Random Generation Algorithm)
Le tableau est utilis pour crypter le message ainsi :
 i = 0
 j = 0
 boucle sur la longueur du message a chiffrer
     i = (i + 1) % 256
     j = (j + S[i]) % 256
     permuter (S[i],S[j])
     k = S[(S[i] + S[j]) % 256]
     message[next] = message[next] XOR k


bl joue le rle de i (le modulo 256 se fait tout seul puisque c'est un
registre 8 bits), al celui de j.
La permutation se fait  ce moment-l :
08048131  888B8C820408      mov [ebx+0x804828c],cl
08048137  88908C820408      mov [eax+0x804828c],dl
et l'octet k est rcupr ici :
0804813D  00D1              add cl,dl
0804813F  8A898C820408      mov cl,[ecx+0x804828c]
puis vient le chiffrement :
08048145  300E              xor [esi],cl

Pour dchiffrer, c'est exactement le mme algorithme (puisque c'est le cas
du xor). Le destinataire du message ralise le KSA depuis la mme cl
(puisqu'elle est symtrique et partage) et applique le PRGA au message
crypt reu.



Ici, la vrification du pass est faite de la faon suivante :
. On initialise RC4 avec la valeur du password donn par l'utilisateur, 
c'est--dire que le 3e call est appel. Celui-ci remplit son rle de KSA.
Ensuite, c'est le tour du 4e call, qui est cens chiffrer (ou dchiffrer) un 
message.
Et bien le message qu'il va traiter, c'est la plage d'octets  partir de 
l'adresse 0x80480af et sur une longueur de 17 octets (puisque la condition
de boucle porte sur edi nul, et qu'il a dmarr  0x11 en tant dcrment
entre chaque loop.

. En sortie, les 4 premiers octets de cette plage (dsol de sortir de tels mots
sachant que certains ne sont peut-tre pas en vacances :P) sont tests pour 
voir s'ils ont t mis  0xbb99090. Si c'est le cas, le crackme est 
officiellement certifi Crack3d, et on jump  cet endroit justement,
car le code dchiffr correspond aux instructions ncessaires pour afficher
Crack3d, et la suite correspond au code de "un jump", qui exit.
Ca correspond  cette partie :
08048091  BEAF800408        mov esi,0x80480af	; esi <- 0x80480af
08048096  AD                lodsd		; EAX <- [ESI]; ESI += 4
08048097  3D9090B90B        cmp eax,0xbb99090	; jmp si eax == 0xbb99090
0804809C  7411              jz 0x80480af	; jmp <=> crack0red



  brute force
  ***********

Au moment o je m'affairais sur ce crackme, je ne connaissais pas les dtails
des algos de RC4,  part la faon dont il est utilis pour WEP ;)
Une fois que j'avais compris le code assembleur, j'ai cherch comment je 
pouvais calculer le pass, commenc  muler le fonctionnement de l'algo en C
pour y voir plus clair, mais ne trouvant rien, et ne sachant mme pas que
c'tait RC4, j'ai prpar un patch pour pouvoir brute-forcer cette saloperie,
et crit  la va-vite un brute-forceur. Mais avant de le laisser mouliner, et 
vu que je pouvais pas le laisser tourner dans les heures qui venaient, j'ai
mail emp pour lui dire o j'en tais, et ce que je m'apprtais  faire,
quelque peu honteux :)
Le lendemain, il m'avait dj rpondu, me disant que c'tait du RC4, et donc
que la seule solution tait de brute-forcer le crackme,  moins de trouver une
faille dans son implmentation ou bien dans l'algo lui-mme.
D'ailleurs, les faiblesses qui permettent de casser du WEP ne sont pas ici
exploitables puisqu'on ne peut pas rcuprer plusieurs messages crypts avec la
mme cl (qui est la bonne) pour raliser du brute-force efficace.
Ici, on connait aussi le message crypt mais on cherche la bonne cl en n'ayant
aucun autre message crypt par elle. On doit donc essayer toutes les cls
possibles, une  une.


Concernant le patch, si on jette a nouveau un oeil sur ce qui precede la 
lecture du pass :

08048169  31DB              xor ebx,ebx
0804816B  43                inc ebx

on voit que bl, qui sera interprt par le code du syscall sys_read comme le
numro du descripteur dans lequel lire, est mis  1 (stdout),
et non  0 (stdin) comme le veut la coutume.
Ainsi, les caractres du pass sont lus lorsqu'ils s'affichent dans le terminal.
Un "echo abcd | ./crk3" n'avance donc  rien, car il faut quand mme taper
"abcd" lorsque le pass est demand (la chaine "abcd" qui est disponible dans
stdin n'est jamais lue).
Adieu le brute force aveugle  peine le crk3 wget'd !

Pour remdier a ca, on veut mettre un nop (opcode 0x90)  la place 
du 0x43 (inc ebx)  l'octet d'adresse virtuelle 0804816B. 
Comme on l'a vu tout  l'heure, cet octet  t manipul durant l'excution 
par l'algorithme qui ralise un xor des opcodes avec la valeur 0xb5.

Pour connatre l'opcode  mettre dans le binaire, on fait :

  0011 0101     35 <- opcode  trouver
^ 1010 0101     b5 <- xor'd avec cette valeur
  ---------
  1001 0000     90 nop <- opcode obtenu aprs passage de l'algo durant l'excution

Il faut donc remplacer l'octet en 0x0804816B pour y mettre la valeur 0x35, ce que
fait crk3_patcher.c.


Ensuite, pour brute-forcer, j'ai fais un bf.sh qui utilise des redirections
dans des fichiers, c'est beaucoup plus rapide de faire :
echo $a$b$c$d | ./crk3.bf >>/data/results
que de faire des choses du genre :
echo $a$b$c$d | ./crk3.bf | grep Crack3d && echo "pass:$a$b$c$d"
 cause du temps de vrification de la valeur de retour du grep je pense.

Dans le 1er cas, le grep est fait entre chaque boucle de la 1re lettre,
c'est--dire trs peu souvent. A noter que le fichier results atteignant
la centaine de megs avant le grep, lorsque ce dernier est fructueux, il
dit juste que a concorde. Il faut alors regarder  la main dans results.

Ce script est loin d'tre optimis, d'ailleurs il a mis 2h30 pour faire 
parcourir  la 1re lettre a-zA-F, lanc en le ralentissant niveau priorit :
$ nice -n 19 bash bf.sh
sur un Athlon 1GHz (256 Mo de RAM).

Enfin aprs en avoir chi sa carte, le PC me donne finalement le password :
m3uh


Une ptite vrif :
$ ./crk3.org

                ---== emp' crackme 3 ==---
                --------------------------

http://afturgurluk.org/~emper0r/
emper0r@secureroot.com

PASSWORD: m3uh

Crack3d !...



w00t w00t ! J'ai entendu quelque chose craquer !


  retour sur les lieux du crime
  *****************************

Apres avoir dit tout a  emp, il m'a confi qu'il s'attendait  ce que l'on
brute force depuis l'intrieur du programme, c'est plus rapide et aussi simple,
puisqu'on a les fonctions de RC4  disposition.
Pour faire les choses jusqu'au bout, et trouvant que c'tait une meilleure ide
et que a pouvait tre fun, je me suis mis  le faire.

Pour faire simple (et rapide car il est 6h et demi du mat l), le fichier
bf.asm contient le code  insrer dans crk3  l'adresse 0x08048054, l o
reposait les instructions du programme principal. Il y a galement un autre
programme qui s'appelle crk3_patchbf.c, charg d'crire le code du brute force
dans crk3, ainsi que de patcher l'entry point pour le faire pointer
en 0x08048054. Les sources sont assez comments pour tre comprhensibles.

Le mode d'emploi (ou la recette ;) est le suivant :
$ nasm -o crk3_autobf.data bf.asm
$ cp crk3.org crk3 ; ./crk3_decoder ; cp crk3_d crk3
$ ./crk3_patchbf
$ ./crk3

FIXME : vrifier que cette solution met moins de temps (c'est un pense-bete pour moi)



Voil c'est fini, j'espre que a vous a intress et que vous avez peut-tre
appris 2, 3 choses. Je te remercie emper0r pour avoir fait ce crkme3, j'ai pris
plaisir  le reverse, et aussi pour m'avoir offert l'occasion de paraitre dans IOC !
Un ptit greetz aussi  ceux que je commence  connatre, et au FRHC continuez
comme a, j'aime bien votre esprit, vivement nc1 !


 - witzy (stazzz@altern.org)
