Les 3 crackme de la NDH 2010
Voici des liens vers les 3 crackme présentés dans les billets précédents :
Write up ndh crackme level 3
- Nom : cm3.exe
- Taille : 213 Ko
- MD5 : ecfb52d387865a9d3d34e9456ad67bdf
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".
Write up ndh crackme level 2
- Nom : cm2.exe
- Taille : 213 Ko
- MD5 : 22979ddc10cfc6f9093de4598af5d7ef
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.
Write up ndh crackme level 1
- Nom : cm1.exe
- Taille : 253 Ko
- MD5 : 9c33b45f25ce622940e47bbc95ecec07
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