UPDATE: Marcin podes艂a艂 mi now膮 binark臋 bez pewnych u艂atwie艅 kt贸rych tam nie mia艂o by膰:
confidence_eset_crackme.zip (binarka bez u艂atwie艅, crackme)
Poniewa偶 adresy w nowej si臋 nie zgadzaj膮 z tym o czym pisze, to na samym dole postu jest do 艣ci膮gni臋cia r贸wnie偶 stara binarka, kt贸rej adresy s膮 zgodne z moim poni偶szym tutorialem :)
Drogi czytelniku, je偶eli chcesz spr贸bowa膰 w艂asnymi si艂ami po艂ama膰 powy偶sze crackme, TO NATYCHMIAST WY艁膭CZ T膭 STRON臉!!! :) Poni偶ej zamieszczam rozwi膮zanie do 贸w crackme.
A
N
T
Y
S
P
O
I
L
E
R
Najpierw par臋 s艂贸w o rozwi膮zaniu: jak pisa艂em w poprzednim po艣cie, mia艂em 3 podej艣cia do tego crackme - za dwoma pierwszymi razami okazywa艂o si臋 偶e "nie t臋dy droga". W rozwi膮zaniu kt贸re przedstawiam poni偶ej pomin臋 etapy pomy艂ek, i przejd臋 od razu do prawid艂owego rozwi膮zania.

Celem crackme jest uzyskanie/odzyskanie has艂a, kt贸re wpisane w pole tekstowe spowoduje wy艣wietlenie czego艣 w stylu "congratz!" (patrz screen powy偶ej). Zaczniemy jak zwykle od rekonesansu - czyli w ruch id膮 narz臋dzia typu PEiD (kt贸rego ofc nie mia艂em przy sobie na konferencji - wzi膮艂em nieskonfigurowanego lapka na confi, prawie bez tools贸w, a ju偶 na pewno bez moich kod贸w ;/) czy Ent (to mia艂em przy sobie). Poni偶ej wrzucam wykres z Enta (osoby zainteresowane ale niezorientowane wtf ten wykres przedstawia odsy艂am do tego postu):

Jak wida膰, nie mamy do czynienia z 偶adnym nietrywialnym szyfrowaniem kodu - good news - wi臋c, nie zwlekaj膮c dalej mo偶emy wrzuci膰 crackme w nasz ulubiony disassembler (czytaj: IDA Pro). Patrz膮c w list臋 Export贸w dowiadujemy si臋 偶e mamy trzy miejsca startowe - 2x TLS callback oraz EP. W tym miejscu na confi zrobi艂em b艂膮d i od razu poszed艂em do EP ignoruj膮c TLSy - 偶eby troch臋 spot臋gowa膰 napi臋cie i teraz tak zrobi臋 :)
Zacznijmy od znalezienia jaki艣 procedurek obs艂uguj膮cych pobieranie tekstu z pola tekstowego - GetWindowTextA, GetDlgItemTextA, etc - czyli przegl膮damy okienko Imports. Jak si臋 okazuje w IAT wyst臋puje jedynie GetDlgItemTextA:
.text:00405B1A GetDlgItemTextA proc near ; CODE XREF: DialogFunc+42
.text:00405B1A jmp ds:__imp_GetDlgItemTextA
.text:00405B1A GetDlgItemTextA endpDodatkowo wida膰 偶e GetDlgItemTextA jest u偶yty jedynie raz - w DialogFunc+42:
.text:00401175 mov ebx, offset String
.text:0040117A push 20
.text:0040117C push 0
.text:0040117E push ebx
.text:0040117F call memset
.text:00401184 push 20 ; cchMax
.text:00401186 push ebx ; lpString
.text:00401187 push 67h ; nIDDlgItem
.text:00401189 push [ebp+hDlg] ; hDlg
.text:0040118C call GetDlgItemTextA
.text:00401191 test eax, eax
.text:00401193 jz short loc_4011AE
.text:00401195 push ebx
.text:00401196 call sub_403055
.text:0040119B test eax, eax
.text:0040119D jz short loc_4011AE
.text:0040119F ; "Congratulations, your password is corre"...
.text:0040119F push offset aCongratulation
.text:004011A4 push [ebp+hDlg] ; hDlg
.text:004011A7 call sub_401132
.text:004011AC jmp short locret_4011C9
.text:004011AE ; ---------------------------------------------------------------------------
.text:004011AE
.text:004011AE loc_4011AE: ; CODE XREF: DialogFunc+49
.text:004011AE ; DialogFunc+53
.text:004011AE ; "Sorry, your password is wrong"
.text:004011AE push offset aSorryYourPassw
.text:004011B3 push [ebp+hDlg] ; hDlg
.text:004011B6 call sub_401132
.text:004011BB jmp short locret_4011C9Pod adresem 00401196 jest call sub_403055, do kt贸rej jest przekazywany string, a nast臋pnie, w zale偶no艣ci od tego co zwr贸ci sub_403055 wy艣wietlany jest napis "Contratulations..." albo "Sorry...". Tak wi臋c sercem ca艂o艣ci jest sub_403055.
Po zej艣ciu do tej funkcji okazuje si臋 ze jest ona d艂uga. Bardzo d艂uga. Baaaaaaaaardzo bardzo d艂uga. A konkretniej, ma jakie艣 3000 linii kodu asma, z czego wi臋kszo艣膰 to instrukcje typu sub, xor, lea, ror, rol, czy add. Pozosta艂a cz臋艣膰 wygl膮da nast臋puj膮co:
.text:00403055 sub_403055 proc near ; CODE XREF: DialogFunc+4C
.text:00403055
.text:00403055 arg_0 = dword ptr 8
.text:00403055
.text:00403055 push ebp
.text:00403056 mov ebp, esp
.text:00403058 push 5
.text:0040305A pop ecx
.text:0040305B mov esi, [ebp+arg_0]
.text:0040305E mov edi, offset go
.text:00403063 pushf
.text:00403064 xor dword ptr ds:[esp], 100h
.text:0040306C popf
.text:0040306D nop
.text:0040306E
.text:0040306E loc_40306E: ; CODE XREF: sub_403055+2A94
.text:0040306E lodsd
.text:0040306F sub eax, 8A14F2F5h
.text:00403074 xor eax, 7418FCC5h
.text:00403079 lea eax, [eax+3A61C552h]
.text:0040307F sub eax, 0D101638Ch
...
.text:00405AE4 ror eax, 17h
.text:00405AE7 stosd
.text:00405AE8 dec ecx
.text:00405AE9 jnz loc_40306E
.text:00405AEF nop
.text:00405AF0 nop
.text:00405AF1 nop
.text:00405AF2 nop
.text:00405AF3 mov edi, offset go
.text:00405AF8 mov esi, offset dword_4070C2
.text:00405AFD push 5
.text:00405AFF pop ecx
.text:00405B00 repe cmpsd
.text:00405B02 setz al
.text:00405B05 and eax, 0FFh
.text:00405B0A leave
.text:00405B0B retn 4
.text:00405B0B sub_403055 endpW skr贸cie, do ESI wrzucany jest adres stringu, do EDI wrzucany jest adres buforu na zakodowany string, a nast臋pnie po 4ry bajty (lodsd) string jest kodowany (max 5*4 czyli 20 bajt贸w), i zapisywany w buforze wyj艣ciowym (stosd). Na samym ko艅cu nast臋puje por贸wnanie zakodowanego stringu z oryginalnym zakodowanym has艂em znajduj膮cym si臋 pod adresem 4070C2:
0FBE0BB50h, 0D16C80CCh, 716786EDh, 3B77A739h, 493A8A5AhA nast臋pnie w zale偶no艣ci czy has艂o jest OK czy nie, zwracana jest pewna warto艣膰.
Wszystko wygl膮da prosto i klarownie na pierwszy rzut oka, zastanowi膰 mog膮 tylko dwie rzeczy:
.text:00403063 pushf
.text:00403064 xor dword ptr ds:[esp], 100h
.text:0040306C popforaz
.text:00405AEF nop
.text:00405AF0 nop
.text:00405AF1 nop
.text:00405AF2 nopPierwszy zastanawiaj膮cy kod to jest w艂膮czenie Trap Flag - czyli trybu krokowego procesora. Na pocz膮tku uzna艂em 偶e to jaki艣 anti-debug, i zignorowa艂em, ale jak si臋 za chwil臋 oka偶e, nic bardziej mylnego!
Drug膮 spraw膮 s膮 4ry NOPy kt贸re ni st膮d ni zow膮d le偶膮 sobie pod koniec funkcji - czy偶by runtime co艣 by艂o tam wrzucane? Jaki艣 dodatkowy kod?
I w tym momencie wr贸cimy do TLS callback贸w, a konkretniej TlsCallback_0 pod adresem 40233A. Od razu rzuci膰 si臋 w oczy mo偶e debug string "loading imports" - jak si臋 okazuje takich string贸w jest wi臋cej, i bardzo dobrze t艂umacz膮 co si臋 dzieje w kodzie. Callback jest kr贸tki, i skupia si臋 na wywo艂aniu paru funkcji, z kt贸rych najciekawsz膮 jest sub_40120E.
W tej偶e funkcji widzimy m.in. CreateProcsss z flag膮 DEBUG_PROCESS, a nast臋pnie p臋tl臋 debuggera kt贸ra odbiera m.in. event EXCEPTION_SINGLE_STEP kt贸ry generowany jest przez w艂膮czenie Trap Flag w kodzie kt贸ry analizowali艣my par臋 linii wy偶ej! No i uk艂adanka zaczyna pasowa膰! Podsumujmy co do tej pory wiemy:
- gdy odpalamy crackme wykonanie nigdy nie dociera do wy艣wietlenia okna - zamiast tego proces odpala ponownie swojego exeka jako debugger!
- czyli mamy dwa procesy
- proces dziecko - kt贸ry wy艣wietla okno, sprawdza has艂o, i w pewnym momencie Trap Flag uaktywnia
- proces rodzica - kt贸ry pozostaje w p臋tli debuggera i czeka a偶 si臋 Trap Flag uaktywni
Oczywistym nast臋pstwem powy偶szego jest fakt i偶 procesu dziecka nie mo偶emy debugowa膰 za pomoc膮 debugger API (stealth debuggery typu Obsidian i debuggery ring 0 ofc dadz膮 rad臋, ale nie s膮 potrzebne tak na prawd臋).
Rzu膰my okiem na obs艂ug臋 eventu SINGLE_STEP:
.text:00401302 mov eax, [CONTEXT.EIP]
.text:00401308 mov edx, [eax]
.text:0040130A cmp edx, 90909090h
.text:00401310 jz short koniec
.text:00401312 or [CONTEXT.EFLAGS], 100h
.text:0040131C cmp dl, 35h ; XOR EAX, ...
.text:0040131F jz short action_xor
.text:00401321 cmp dl, 2Dh ; SUB EAX, ...
.text:00401324 jz short action_sub
.text:00401326 cmp dl, 5 ; ADD EAX, ...
.text:00401329 jz short action_add
.text:0040132B jmp short koniec
.text:0040132D ; ---------------------------------------------------------------------------
.text:0040132D
.text:0040132D action_xor: ; CODE XREF: sub_40120E+111
.text:0040132D sub dword ptr [CONTEXT.EAX], 2
.text:00401334 jmp short koniec
.text:00401336 ; ---------------------------------------------------------------------------
.text:00401336
.text:00401336 action_sub: ; CODE XREF: sub_40120E+116
.text:00401336 add dword ptr [CONTEXT.EAX], 1
.text:0040133D jmp short koniec
.text:0040133F ; ---------------------------------------------------------------------------
.text:0040133F
.text:0040133F action_add: ; CODE XREF: sub_40120E+11B
.text:0040133F xor dword ptr [CONTEXT.EAX], 10101010h
.text:00401349
.text:00401349 koniec: ; CODE XREF: sub_40120E+102
.text:00401349 ; sub_40120E+11D ...Dzia艂anie tego mechanizmu jest nast臋puj膮ce (je偶eli kto艣 czyta艂 m贸j art z Xploit 3/2008 o user opcodes to dostrze偶e podobie艅stwo :>):
- spod EIP procesu dziecka pobierane s膮 4ry bajty instrukcji
- je偶eli te 4 bajty to 90909090 (czyli 4x NOP - brzmi znajomo ?) to nic wi臋cej nie jest robione
- w innym wypadku flaga TF jest odnawiana (TF jest gaszone po pierwszym 'uruchomieniu')
- a nast臋pnie analizowany jest pierwszy bajt instrukcji spod EIP:
-- je偶eli jest to 35 (czyli XOR EAX, imm32), to dodatkowo od EAX odejmowane jest 2 (to si臋 wykonuje PRZED prawdziw膮 instrukcj膮)
-- je偶eli jest to 2D (czyli SUB EAX, imm32), to dodatkowo do EAX dodawane jest 1
-- je偶eli jest to 05 (czyli ADD EAX, imm32), to dodatkowo EAX jest xorowane z 10101010h
- wykonanie procesu jest kontynuowane
Czyli - metoda szyfruj膮ca has艂o zawiera dodatkowe dodawania/odejmowania/xorowania kt贸rych nie wida膰 w listingu (bo wykonuje je zew debugger)! Sprryyytne :)
OK. Teraz mamy ju偶 wszystkie klocki potrzebne do rozwi膮zania zagadki! Metod na odzyskanie has艂a jest kilka - np. mo偶na odwr贸ci膰 listing, albo zrobi膰 brute force - ja zdecydowa艂em si臋 na to ostatnie.
W takim wypadku nale偶y zacz膮膰 od skopiowania ca艂ej procedury zawieraj膮cej kodowanie has艂a, a nast臋pnie kilkoma regexpami wklei膰 dodatkowe dec+dec, inc czy xor (najlepiej robi膰 to w艂a艣nie w tej kolejno艣ci). Potem mo偶na usun膮膰 niepotrzebne ju偶 w艂膮czanie TF, i skompilowa膰.
Ostateczna posta膰 procedury: esetcode.nasm
Teraz klepiemy w C/C++ prosty bruteforce kt贸ry ma:
- wczyta膰 do pami臋ci skompilowan膮 wersj臋 procedury
- poprawi膰 w niej adresy
- a nast臋pnie, korzystaj膮c z faktu 偶e jest tylko 4GB kombinacji minus kombinacje zawieraj膮ce przynajmniej jeden znak niedrukowalny, odpala膰 procedur臋 szyfruj膮c膮 has艂o
Poniewa偶 has艂o szyfrowane jest DWORDami kt贸re s膮 dodatkowo niezale偶ne od siebie, tak wi臋c mo偶emy w jednej p臋tli szuka膰 prawid艂owego rozwi膮zania dla wszystkich pi臋ciu DWORD贸w z has艂a. Taki brute force wygl膮da nast臋puj膮co (kod pisany na kolanie na konkursie, wi臋c nie spodziewajcie sie miss code 2009 ;p):
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[1024 * 1024];
DWORD a[5];
DWORD b[5];
int
main(void)
{
FILE *f;
f = fopen("code", "rb");
fread(buf, 1, sizeof(buf), f);
fclose(f);
int (__stdcall *func)(const char *a) = (typeof(func))&buf;
#define P1 0xd
#define P2 0x34B8
#define P3 0x34BD
DWORD v12 = (DWORD)a;
DWORD v3 = (DWORD)b;
*(DWORD*)(P1 + buf) = v12;
*(DWORD*)(P2 + buf) = v12;
*(DWORD*)(P3 + buf) = v3;
puts("done patching"); fflush(stdout);
static unsigned char brute[40];
DWORD myin;
for(myin = 0; myin != 0xffffffff; myin++)
{
*(DWORD*)brute = myin;
if((myin % 0x01000000) == 0)
putchar('.');
if(brute[0] < ' ' || brute[0] > '~') continue;
if(brute[1] < ' ' || brute[1] > '~') continue;
if(brute[2] < ' ' || brute[2] > '~') continue;
if(brute[3] < ' ' || brute[3] > '~') continue;
func((const char*)brute);
if(a[0] == 0x0FBE0BB50 || a[0] == 0x0D16C80CC || a[0] == 0x716786ED || a[0] == 0x3B77A739 || a[0] == 0x493A8A5A)
{
char asdf[8];
*(DWORD*)(asdf) = myin;
asdf[4] = 0;
printf("%.8x (%s) == %.8x\n", myin, asdf, a[0]);
}
}
return 0;
}Kompilujemy (g++, z uwagi na u偶ycie typeof()), odpalamy, i... (ilo艣膰 kropek mo偶e nie odpowiada膰 rzeczywisto艣ci ;p)
done patching
.................................20276e69 (in' ) == 716786ed
20756f59 (You ) == fbe0bb50
........................................6b6c6174 (talk) == d16c80cc
..6d206f74 (to m) == 3b77a739
......................................................Jak wida膰 nie znalaz艂o ostatniego ci膮gu (\0 by trzeba tam w brute dorzuci膰), ale to ma艂o istotne. Uk艂adaj膮c to co mamy mo偶emy si臋 domy艣li膰 co jest ostatnie: You talkin' to m -> You talkin' to me?, i gotow臋! :)
No i chyba tyle :)
P.S. Oryginalna binarka z CONFidence: confiesetcrackme.zip


Comments:
BTW, Czemu ludzie od security i nie tylko pisza taki paskudny kod?
Ani to C ani C++, tylko miksy, C z klasami lub inne potworki ;p
To przez ewolucj臋! -> http://www.kaila.pl/humor/program.htm ;D
Gratz za XSS'a (mimo 偶e trzeba klikn膮膰) ;> To pierwsza rzecz kt贸r膮 kto艣 znalaz艂 na moim blogu ;> Zaraz to spatchuje ;>
@flame
Pozwoli艂em sobie ukry膰 flamewar jaki wywi膮za艂 si臋 mi臋dzy bw a innymi czytelnikami ;>
Natomiast dla cel贸w kronikarskich zaznacz臋 o co chodzi艂o: bw stwierdzi艂 偶e crackme by艂o zbyt proste i 偶e powinno by膰 trudniejsze, na co marcin odpar艂 偶e tak mia艂o by膰 jak by艂o (i 偶e crackme by艂o przewidziane na 30 minut), a potem dyskusja zesz艂a na to co powinno by膰 dozwolone podczas crackme, a czego u偶ywania mo偶na zabroni膰, co sko艅czy艂o si臋 og贸lnymi wjazdami personalno/firmowymi :)
Sure ;> Prosta sprawa - w URL w komentarzach wpisa膰 np. javascript:alert(666), i potem czeka膰 a偶 kto艣 kliknie ;>
2-3 minuty z tego co pami臋tam ;)
Add a comment: