Kuidas ma lahendasin lihtsa CrackMe väljakutse NSA Ghidraga

Tere!

Olen hiljuti natuke mänginud Ghidraga, mis on pöördprojekteerimise tööriist, mille hiljuti hankis NSA. Ametlikul veebisaidil kirjeldatakse tööriista järgmiselt:

Tarkvara pöördtehnoloogia (SRE) tööriistakomplekt, mille NSA teadusdirektoraat on välja töötanud küberturvalisuse missiooni toetamiseks.

Olen oma pöördinseneri karjääri alguses, nii et ma ei teinud midagi edasijõudnut. Ma ei tea, milliseid funktsioone selliselt professionaalselt tööriistalt oodata on, kui soovite lugeda Ghidra täpsemate funktsioonide kohta, pole see tõenäoliselt teie jaoks mõeldud artikkel.

Selles artiklis püüan lahendada lihtsa CrackMe väljakutse, mille olen leidnud veebisaidilt root-me. Väljakutse, mille ma lahendan, kannab nime ELF - CrackPass. Kui soovite seda ise proovida, peaksite kaaluma selle artikli mitte lugemist, sest see rikub teie väljakutset.

Alustame! Avan Ghidra ja loon uue projekti, mida nimetan RootMe-ks.

Seejärel impordin väljakutsefaili, lohistades selle projekti kausta. Lähen vaikimisi.

Pärast kahendfaili kohta teabe esitamist vajutan nuppu OK, valin faili ja topeltklõpsake seda. See avab Ghidra koodibrauseri utiliidi ja küsib, kas ma tahan faili analüüsida, siis vajutan Jah ja jätkan vaikeseadetega.

Pärast faili importimist saame teavet kahendfaili kohta. Kui vajutame nuppu OK ja jätame selle akna kõrvale ning topeltklõpsame imporditud failil, avaneb see Ghidra koodibrauseri utiliit. Valin Jah, kui palutakse binaarsust analüüsida ja jätkata vaikeseadetega.

Koodibrauser on üsna mugav. Vasakul paneelil näeme demonteerimisvaadet ja paremal paneelil dekompileerimisvaadet.

Ghidra näitab meile otse ELF-i päise teavet ja binaarse sisestuspunkti. Pärast sisestuspunkti topeltklõpsamist hüppab dissembleri vaade sisestusfunktsioonile.

Nüüd saame edukalt tuvastada põhifunktsiooni, mille nimetan ümber peamiseks. Oleks tore, kui tööriist üritaks põhifunktsiooni automaatselt tuvastada ja selle vastavalt ümber nimetada.

Enne põhifunktsiooni analüüsimist tahtsin selle allkirja muuta. Muutsin tagastuse tüübi int ja parandasin parameetrite tüübi ja nime. See muudatus on jõustunud dekompileerimisvaates, mis on lahe! ?

Dekompileerimisvaates joone esiletõstmine tõstab selle esile ka monteerimisvaates.

Uurime funktsiooni FUN_080485a5, mille nimetan ümber CheckPasswordiks.

Funktsiooni CheckPassword sisu leiate allpool. Olen koodi kopeerinud otse Ghidra dekompileerimisvaates, mis on puhas funktsioon, millest paljudel seda tüüpi tööriistadel puudub! Komplekti ja koodi kopeerimine on tore funktsioon.

void CheckPassword(char *param_1) { ushort **ppuVar1; int iVar2; char *pcVar3; char cVar4; char local_108c [128]; char local_100c [4096]; cVar4 = param_1; if (cVar4 != 0) { ppuVar1 = __ctype_b_loc(); pcVar3 = param_1; do { if (((byte )(ppuVar1 + (int)cVar4) & 8) == 0) { puts("Bad password !"); /* WARNING: Subroutine does not return */ abort(); } cVar4 = pcVar3[1]; pcVar3 = pcVar3 + 1; } while (cVar4 != 0); } FUN_080484f4(local_100c,param_1); FUN_0804851c(s_THEPASSWORDISEASYTOCRACK_08049960,local_108c); iVar2 = strcmp(local_108c,local_100c); if (iVar2 == 0) { printf("Good work, the password is : \n\n%s\n",local_108c); } else { puts("Is not the good password !"); } return; }

Pärast koodiga tutvumist olen jõudnud järgmiste järeldusteni. Blokk ifkontrollib, kas kasutaja on sisestanud parooli, ja kontrollib antud parooli, et kontrollida, kas see on kehtiv märk või midagi muud. Ma pole täpselt kindel, mida see kontrollib, kuid siin on, mida ütleb __ctype_b_loc ():

Funktsioon __ctype_b_loc () tagastab kursori praeguses lokaadis olevate tähemärkide massiivi, mis sisaldab praeguse märgistiku iga märgi omadusi. Massiiv sisaldab kokku 384 tähemärki ja seda saab indekseerida kõigi allkirjastatud või allkirjastamata tähemärkidega (st indeksi väärtusega vahemikus 128 kuni 255). Kui rakendus on mitmekeermeline, peab massiiv olema praeguse lõime lokaalne.

Igatahes pole see koodiplokk aega väärt, sest see ei muuda meie parooli mitte mingil moel, vaid lihtsalt kontrollib seda. Nii et võime sellise kontrollimise vahele jätta.

Järgmine funktsioon, mida nimetatakse, on FUN_080484f4. Selle koodi vaadates võime öelda, et see on lihtsalt kohandatud memkoopia rakendamine. Selle asemel, et kopeerida D-kood dekompilaatori vaates, kopeerisin montaažikoodi - jah, see on lõbus.

************************************************************* * FUNCTION ************************************************************* undefined FUN_080484f4 (undefined4 param_1 , undefined4 p undefined AL:1  undefined4 Stack[0x4]:4 param_1 XREF[1]: 080484f8 (R) undefined4 Stack[0x8]:4 param_2 XREF[1]: 080484fb (R) FUN_080484f4 XREF[1]: CheckPassword:080485f5 (c) 080484f4 55 PUSH EBP 080484f5 89 e5 MOV EBP ,ESP 080484f7 53 PUSH EBX 080484f8 8b 5d 08 MOV EBX ,dword ptr [EBP + param_1 ] 080484fb 8b 4d 0c MOV ECX ,dword ptr [EBP + param_2 ] 080484fe 0f b6 11 MOVZX EDX ,byte ptr [ECX ] 08048501 84 d2 TEST DL,DL 08048503 74 14 JZ LAB_08048519 08048505 b8 00 00 MOV EAX ,0x0 00 00 LAB_0804850a XREF[1]: 08048517 (j) 0804850a 88 14 03 MOV byte ptr [EBX + EAX *0x1 ],DL 0804850d 0f b6 54 MOVZX EDX ,byte ptr [ECX + EAX *0x1 + 0x1 ] 01 01 08048512 83 c0 01 ADD EAX ,0x1 08048515 84 d2 TEST DL,DL 08048517 75 f1 JNZ LAB_0804850a LAB_08048519 XREF[1]: 08048503 (j) 08048519 5b POP EBX 0804851a 5d POP EBP 0804851b c3 RETComment: param_1 is dest, param_2 is src. 08048501 checks if src is null and if it is it returns else it initializes EAX (index, current_character) with 0. The next instructions move bytes into EBX (dest) from EDX (src).The loop stops when EDX is null.

Ja teine ​​funktsioon FUN_0804851c genereerib parooli stringist „THEPASSWORDISEASYTOCRACK”. Dekompileeritud vaadet vaadates. võime umbes näha, kuidas see funktsioon töötab. Kui meil seda ei oleks, peame kõiki funktsiooni monteerimisjuhiseid käsitsi analüüsima, et mõista, mida see teeb.

Seejärel võrdleme varem loodud parooli kasutajalt saadud parooliga (esimene argument argv [1]). Kui see sobib, ütleb programm hea töö ja prindib selle, muidu prindib veateate.

Selle põhianalüüsi põhjal võime järeldada, et kui lappime programmi erinevates kohtades, võime selle parooli sülitada ilma, et oleks vaja ühtegi C-funktsiooni tühistada ja koodi kirjutada. Programmi parandamine tähendab mõne selle juhise muutmist.

Vaatame, mida peame lappima:

Aadressil 0x0804868c lappime JNS-i juhised JMP-ks. Ja voilà, muudatus kajastub dekompilaatori vaates. Ptrace tulemuse kontrollist möödutakse.

{ ptrace(PTRACE_TRACEME,0,1,0); if (argc != 2) { puts("You must give a password for use this program !"); /* WARNING: Subroutine does not return */ abort(); } CheckPassword(argv[1]); return 0;}

Aadressil 0x080485b8 lappime JZ-i juhised JMP-i. Me möödume sellest paroolide kinnitamise blokist, mida nägime varem.

void CheckPassword(undefined4 param_1) { int iVar1; char local_108c [128]; char local_100c [4096]; CustomCopy(local_100c,param_1); GeneratePassword(s_THEPASSWORDISEASYTOCRACK_08049960,local_108c); iVar1 = strcmp(local_108c,local_100c); if (iVar1 == 0) { printf("Good work, the password is : \n\n%s\n",local_108c); } else { puts("Is not the good password !"); } return; }

Aadressil 0x0804861e lappime JNZ JZ-i. See pöörab tingimuse if / else. Kuna me ei tea parooli, esitame juhusliku parooli, mis pole võrdne genereeritud parooliga, käivitades printf teise ploki peal.

void CheckPassword(undefined4 param_1) { int iVar1; char local_108c [128]; char local_100c [4096]; CustomCopy(local_100c,param_1); // constructs the password from the strings and stores it in // local_108c GeneratePassword(s_THEPASSWORDISEASYTOCRACK_08049960,local_108c); iVar1 = strcmp(local_108c,local_100c); if (iVar1 == 0) { // passwords are equal puts("Is not the good password !"); } else { printf("Good work, the password is : \n\n%s\n",local_108c); } return; }

See on kõik!

Nüüd käivitame programmi. Muudes tööriistades me faili lihtsalt salvestame ja see töötab, kuid Ghidras näib, et peame selle eksportima.

Programmi eksportimiseks läheme File -> Export Program (O). Muudame vormingu binaarseks ja klõpsame nuppu OK.

Ma saan eksporditud programmi oma töölauale, kuid see ei tööta - ma ei suutnud eksportitud programmi käivitada. Pärast seda, kui proovisin selle päist programmi readelf-h lugeda, saan järgmise väljundi:

[email protected]:/mnt/c/users/denis/Desktop# readelf -h Crack.bin ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x8048440 Start of program headers: 52 (bytes into file) Start of section headers: 2848 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 7 Size of section headers: 40 (bytes) Number of section headers: 27 Section header string table index: 26 readelf: Error: Reading 1080 bytes extends past end of file for section headers

Häbi. Näib, et Ghidra on failipäise segi ajanud ... ja praegu ei taha ma päiseid käsitsi parandada. Niisiis käivitasin veel ühe tööriista ja rakendasin failile samad plaastrid, salvestasin selle, käivitasin juhusliku argumendiga ja kinnitasin lipu.

Järeldused

Ghidra on kena ja suure potentsiaaliga tööriist. Praeguses seisus pole see nii suurepärane, kuid töötab. Olen oma sülearvutis töötades kohanud ka imelikku kerimisviga.

Alternatiiviks oleks maksta teiste sedalaadi tööriistade eest dollarit, teha ise tööriistu või töötada tasuta, kuid mitte nii kasutajasõbralike tööriistadega.

Loodame, et kui kood on välja antud, hakkab kogukond parandusi tegema ja Ghidrat täiustama.

Täname lugemast!