20/06/2010

Les 3 crackme de la NDH 2010

Voici des liens vers les 3 crackme présentés dans les billets précédents :

cm1.exe cm2.exe cm3.exe

20/06/2010

Write up ndh crackme level 3

Ici on a à faire a un anti-debugger, en regardant GetModuleH dans les références de notre module actuel, on tombe sur :

00401060   > 68 0C934000    PUSH cm3.0040930C                  ; /ProcNameOrOrdinal = "IsDebuggerPresent"
00401065   . 68 20934000    PUSH cm3.00409320                        ; |/pModule = "Kernel32.dll"
0040106A   . FF15 6C804000  CALL DWORD PTR DS:[<&KERNEL32.GetModuleH>; |\GetModuleHandleA
00401070   . 50             PUSH EAX                                 ; |hModule
00401071   . FF15 74804000  CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>; \GetProcAddress
00401077   . FFD0           CALL EAX
00401079   . 33DB           XOR EBX,EBX
0040107B   . 85C0           TEST EAX,EAX
0040107D   . 74 07          JE SHORT cm3.00401086

On patch le saut conditionnel JE -> JMP et hop ça marche on peut lancer notre exécutable tranquillement. On suit le thread qui vient d'être cré et on va venir chercher notre fameuse référence GetWindowTextA :

00401570   . FF15 58814000  CALL DWORD PTR DS:[<&USER32.GetWindowTextA>]        ; \GetWindowTextA

Ça devient pareil ces challenges on regarde plus bas :

004015AF   . 83F8 10        CMP EAX,10
004015B2   . 0F85 7A000000  JNZ cm3.00401632

Donc ici notre password devra être d'une longueur de 16 ( 0x10 ) caractères. Prenons le password suivant comme point de départ : "abcdefghijklmnop". Plus loin on voit l'appel à une routine :

004015BD   . FF15 B0C64000  CALL DWORD PTR DS:[40C6B0]

Si on va voir ce qui se passe dedans on peut voir plusieurs sauts conditonnels vers 003800D5 qui fera échouer l'authentification. Restons sur notre exemple "abcdefghijklmno" :

00380011   8A42 03          MOV AL,BYTE PTR DS:[EDX+3]  << 'd'
00380014   8A22             MOV AH,BYTE PTR DS:[EDX]  << 'a'
00380016   3AC4             CMP AL,AH  << 'd' == 'a'
00380018   0F85 B7000000    JNZ 003800D5
0038001E   8A42 01          MOV AL,BYTE PTR DS:[EDX+1]  << 'b'
00380021   8A62 08          MOV AH,BYTE PTR DS:[EDX+8]  << 'i'
00380024   3AC4             CMP AL,AH  << 'b' == 'i'
00380026   0F85 A9000000    JNZ 003800D5
0038002C   8A42 05          MOV AL,BYTE PTR DS:[EDX+5]   << 'f'
0038002F   3C 72            CMP AL,72   << 'f' == 0x72 'r'
00380031   0F85 9E000000    JNZ 003800D5
00380037   8A42 04          MOV AL,BYTE PTR DS:[EDX+4]   << 'e'
0038003A   3C 65            CMP AL,65   << 'e' == 0x65 'e'
0038003C   0F85 93000000    JNZ 003800D5
00380042   8A42 0B          MOV AL,BYTE PTR DS:[EDX+B]    << 'l'
00380045   8A62 05          MOV AH,BYTE PTR DS:[EDX+5]   << 'f'
00380048   3AC4             CMP AL,AH   << 'l' == 'f'
0038004A   0F85 85000000    JNZ 003800D5
00380050   8A42 0A          MOV AL,BYTE PTR DS:[EDX+A]    << 'k'
00380053   8A62 04          MOV AH,BYTE PTR DS:[EDX+4]    << 'e'
00380056   3AC4             CMP AL,AH   << 'k' == 'e'
00380058   75 7B            JNZ SHORT 003800D5
0038005A   8A42 03          MOV AL,BYTE PTR DS:[EDX+3]  << 'd'
0038005D   3C 6C            CMP AL,6C   << 'd' == 0x6c 'l'
0038005F   75 74            JNZ SHORT 003800D5
00380061   8A42 01          MOV AL,BYTE PTR DS:[EDX+1]   << 'b'
00380064   3C 61            CMP AL,61   << 'b' == 0x61 'a'
00380066   75 6D            JNZ SHORT 003800D5
00380068   8A42 02          MOV AL,BYTE PTR DS:[EDX+2]   << 'c'
0038006B   8A62 07          MOV AH,BYTE PTR DS:[EDX+7]   << 'h'
0038006E   32C4             XOR AL,AH
00380070   3C 1C            CMP AL,1C    << 'c' ^ 'h' == 0x1c 
00380072   75 61            JNZ SHORT 003800D5
00380074   8A42 02          MOV AL,BYTE PTR DS:[EDX+2]
00380077   24 63            AND AL,63
00380079   8A5A 07          MOV BL,BYTE PTR DS:[EDX+7] 
0038007C   80E3 63          AND BL,63
0038007F   3AC3             CMP AL,BL
00380081   75 52            JNZ SHORT 003800D5
00380083   8A42 09          MOV AL,BYTE PTR DS:[EDX+9]  << 'j'
00380086   2C 10            SUB AL,10    << x = 'j' - 10 
00380088   8A5A 06          MOV BL,BYTE PTR DS:[EDX+6]   << 'g'
0038008B   80C3 11          ADD BL,11  << y = 'g' + 11
0038008E   3AC3             CMP AL,BL     << x == y
00380090   75 43            JNZ SHORT 003800D5
00380095   8A42 07          MOV AL,BYTE PTR DS:[EDX+7]  << 'h'
00380098   2C 07            SUB AL,7    << x = 'h' - 7
0038009A   8A5A 06          MOV BL,BYTE PTR DS:[EDX+6]   << 'g'
0038009D   80C3 11          ADD BL,11   << y ='g' + 11
003800A0   3AC3             CMP AL,BL   << x == y
003800A2   75 31            JNZ SHORT 003800D5
003800A4   8A42 02          MOV AL,BYTE PTR DS:[EDX+2]  << 'c'
003800A7   3C 77            CMP AL,77   << 'c' = 0x77 'w'
003800A9   75 2A            JNZ SHORT 003800D5
003800AB   8A42 0E          MOV AL,BYTE PTR DS:[EDX+E]  << 'o'
003800AE   8A62 0D          MOV AH,BYTE PTR DS:[EDX+D]  << 'n'
003800B1   3AC4             CMP AL,AH     << 'n' == 'o'
003800B3   75 20            JNZ SHORT 003800D5
003800B5   8A42 0F          MOV AL,BYTE PTR DS:[EDX+F]   << 'p'
003800B8   2C 37            SUB AL,37    << 'p' - 37
003800BA   3C 00            CMP AL,0     << 'p' = 0x37 '7'
003800BC   75 17            JNZ SHORT 003800D5
003800BE   8A42 0E          MOV AL,BYTE PTR DS:[EDX+E]   << 'o'
003800C1   2C 33            SUB AL,33     << 'o' - 33
003800C3   3C 00            CMP AL,0      << 'o' = 0x33 '3'
003800C5   75 0E            JNZ SHORT 003800D5
003800C7   8A42 0C          MOV AL,BYTE PTR DS:[EDX+C]   << 'm'
003800CA   2C 31            SUB AL,31    << 'm' - 31
003800CC   3C 00            CMP AL,0     << 'm' = 0x31 '1'
003800CE   75 05            JNZ SHORT 003800D5

Calcul de 'h' :

>>> hex(0x77 ^ 0x1c)
'0x6b'

Table de translation :

a b c d e f g h i j k l m n o p
l a w l e r S k a t e r 1 3 3 7

Password : "lawlerSkater1337".

20/06/2010

Write up ndh crackme level 2

Allez, c'est reparti ! On ouvre notre exécutable avec ollydbg sans se poser de questions. Ici, même technique que le précédent on va aller chercher nos références à GetWindowTextA :

References in cm2:.text to <&USER32.GetWindowTextA>, item 0

Address=00401885
Disassembly=CALL DWORD PTR DS:<&USER32.GetWindowTextA>
Comment=USER32.GetWindowTextA

On vient poser notre breakpoint habituel en :

00401885   .  FF15 50814000 CALL DWORD PTR DS:[<&USER32.GetWindowTex>; \GetWindowTextA

On step over et on voit directement :

004018A9   .  83F8 08       CMP EAX,8
004018AC   .  75 7C         JNZ SHORT cm2.0040192A

Donc le password ( serial ) devra être d'une longueur de 8 caractères. Repartons de zéro et prenons comme password de test : "12345678". Plus loin on peut voir une routine qui est appelée :

004018BC   .  E8 3FF7FFFF   CALL cm2.00401000
004018C1   .  83C4 04       ADD ESP,4
004018C4   .  85C0          TEST EAX,EAX
004018C6   .  74 62         JE SHORT cm2.0040192A

Avec un saut conditionnel pour le code de retour qui sera testé et nous dira si le password est valide ou non.

Elle fait quoi cette routine en 00401000 ?

00401000   $  55            PUSH EBP
00401001   .  8BEC          MOV EBP,ESP
00401003   .  53            PUSH EBX
00401004   .  33D2          XOR EDX,EDX
00401006   .  33C0          XOR EAX,EAX
00401008   .  33DB          XOR EBX,EBX
0040100A   .  33C9          XOR ECX,ECX
0040100C   .  A1 B4B64000   MOV EAX,DWORD PTR DS:[40B6B4]
00401011   .  8BDA          MOV EBX,EDX
00401013   .  C1E3 0C       SHL EBX,0C
00401016   .  03C3          ADD EAX,EBX
00401018   .  8BDD          MOV EBX,EBP
0040101A   .  83C3 08       ADD EBX,8
0040101D   .  8B1B          MOV EBX,DWORD PTR DS:[EBX]
0040101F   .  03DA          ADD EBX,EDX
00401021   .  8A0B          MOV CL,BYTE PTR DS:[EBX]
00401023   .  33DB          XOR EBX,EBX
00401025   .  03D9          ADD EBX,ECX
00401027   .  03D9          ADD EBX,ECX
00401029   .  03D9          ADD EBX,ECX
0040102B   .  03D9          ADD EBX,ECX
0040102D   .  03D9          ADD EBX,ECX
0040102F   .  03C3          ADD EAX,EBX
00401031   .  42            INC EDX
00401032   .- FFE0          JMP EAX
00401034   .  33C0          XOR EAX,EAX
00401036   .  5B            POP EBX
00401037   .  5D            POP EBP
00401038   .  C3            RET

La première chose à voir est la construction du registre EAX à l'aide des différents opérandes ADD vers lesquels on jumpera ( JMP EAX ).

Si on se balade dans la routine on se rend compte qu'elle construit EAX à partir d'un pointeur en 40B6B4 ( 003D000 : sa valeur initiale ) auquel on ajoute 5 fois la valeur du caractère courant. On voit plus loin que lorsque l'on va revenir ( return ) de notre jump il y aura un XOR EAX qui fera merder le test conditionnel vu ci-dessus.

Le but serait donc d'éviter de revenir sur le XOR. Regardons où on jump.

003D014F   /E9 AC2E0000     JMP 003D3000
003D0154   |E9 A73E0000     JMP 003D4000
003D0159   |E9 A24E0000     JMP 003D5000
003D015E   |E9 9D5E0000     JMP 003D6000
003D0163   |E9 986E0000     JMP 003D7000
003D0168  ^|E9 93FEFFFF     JMP 003D0000
003D016D   |E9 8E0E0000     JMP 003D1000

C'est pas beau à voir le disas par ici.Bon ! Sautons en 003D3000 , enchaînons les sauts consécutifs jusqu'à arriver à :

003D7000  - E9 2FA00200     JMP cm2.00401034
003D7005  - E9 2AA00200     JMP cm2.00401034
003D700A  - E9 25A00200     JMP cm2.00401034
003D700F  - E9 20A00200     JMP cm2.00401034

Tiens, tiens ! Des sauts vers notre XOR qui fera tout merder. En regardant en détail tous les sauts qu'il y avait dans la liste sur laquelle on vient de tomber, on en voit 1 différent des autres :

003D70FF  - E9 329F0200     JMP cm2.00401036

Ce saut nous emmènerait juste après le XOR en question, c'est exactement ce qu'il nous faut. Là je me suis dit qu'en construisant EAX de manière à avoir EAX=003D70FF ce serait parfait !

Mais c'est impossible en un passage ( à partir de 5 additions du code hexa du premier caractère de la chaîne de passer de 0x0003D0000 à 0x003D70FF ).

Je relance les tests et je pose un breakpoint sur le JMP EAX en question; On step into et on voit :

003D0145   /E9 B60E0000     JMP 003D1000
003D014A  -|E9 B70E0300     JMP cm2.00401006
003D014F   |E9 AC2E0000     JMP 003D3000

Si EAX avait été égal à 0x003D014A au premier passage, on serait retombé sur la routine et peut-être arrivé à créer la valeur de EAX que l'on souhaite ( 0x003D70FF )

Bien cherchons tous les références à ce jump ( JMP cm2.00401006 ), logiquement il devrait être référencé 7 fois.

References in 003D0000..003D9FFF to cm2.00401006
Address    Disassembly
003D014A   JMP cm2.00401006
003D120D   JMP cm2.00401006
003D2226   JMP cm2.00401006
003D31E5   JMP cm2.00401006
003D423A   JMP cm2.00401006
003D525D   JMP cm2.00401006
003D612C   JMP cm2.00401006

Si on remarque bien à chaque saut l'addresse est incrementée de ( 0x1000 ) en plus des 5 additions de la valeur du caractère. Remontez et regardez le code en 00401011 : A chaque tour la valeur de EAX avant les additions est incrémentée de 0x1000 à chaque passage dans la routine. 1er passage : 0x003D0000 2ème passage : 0x003D1000 etc..

Logiquement il ne nous reste quasi rien à faire pour trouver notre password, on va calculer à la main la valeur que devrait avoir chaque caractère pour bien répondre aux critères des différents sauts qu'il va falloir enchaîner.

>>> chr(0x14a/5)
'B'
>>> chr(0x20d/5)
'i'
>>> chr(0x226/5)
'n'
>>> chr(0x1e5/5)
'a'
>>> chr(0x23a/5)
'r'
>>> chr(0x25d/5)
'y'
>>> chr(0x12c/5)
'<'

Mais nous n'avons que 7 caracatères pour l'instant souvenez vous :

003D70FF - E9 329F0200 JMP cm2.00401036

C'est l'adresse sur laquelle nous voulions jumper :]

>>> chr(0x0ff/5)
'3'

On teste le password "Binary<3" et c'est dans la poche.

20/06/2010

Write up ndh crackme level 1

On lance directement notre application dans Ollydbg. On va regarder la liste de tous les noms symboliques ( CTRL + N ) et on va s'intéresser à toutes les références de GetWindowTextA.

On a ses 2 références :

00401036   JMP DWORD PTR DS:[<&USER32.GetWindowText  USER32.GetWindowTextA
00412082   CALL DWORD PTR DS:[<&USER32.GetWindowTex  USER32.GetWindowTextA

La première n'est pas importante et n'est pas celle qui nous intéresse, dirigeons nous vers la deuxième :

00412082   .  FF15 90314100 CALL DWORD PTR DS:[<&USER32.GetWindowTex>; \GetWindowTextA

Posons un breakpoint pour vérifier tout cela ( F2 ). Appuyons sur le bouton ok en laissant le texte par défaut : "CrAck Me". Ça break bien, on va step over ( F8 ) et regarder ce qui se passe :

004120AF   . /0F84 79000000 JE cm1.0041212E

Juste après le saut conditionnel vérifiant que l'on ait bien rentré quelque chose on voit :

004120B5   .  BA BC764100   MOV EDX,cm1.004176BC   ;  ASCII "qoellao"

Si en passant sur cette instruction on regarde dans la fenêtre de rappel des opérandes, on voit :

004176BC=cm1.004176BC (ASCII "roflmao")
EDX=0012FB05

Si on regarde le code qui suit on comprend vite que notre input va être comparé à ce mot, il ne reste plus qu'à le tester.

Et voila on récupére le hash pour la validation : daa2a4c70f78f028720e66ece1e30365.

Pages : 1