................... ...::: phearless zine #2 :::... ................>---[ Writing Linux Shellcode - Basics ]---<................ .........................>---[ by Shatterhand ]---<......................... shatterh[at]gmail[dot]com Sadrzaj: [1] Intro - Bytecode theory [2] Asm basics <2.1> Registers <2.2> Instructions [3] Using sys calls [4] Writing shellcode <4.1> Tools <4.2> setreuid()/execve() <4.3> Null byte headache <4.4> Optimizing <4.5> Few words about socket calls [5] Close up & further reading /////////////////////////////////////////////////////////////////////////// --==<[ 1. Intro - Bytecode theory \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Poslednjih nekoliko godina susretali smo se sa raznolikim propustima u velikom broju aplikacija. Najceshce su to bili buffer i heap overflow-i, format string bugovi i slicni. Metode i tehnike exploitanja su se vremenom menjale ali neke stvari su ostale iste. Medju tim stvarima svoje mesto pronalazi i bytecode. Bytecode se godinama koristi u exploitanju nekih od gore pomenutih propusta(npr. buffer overflow) i kao takav njegovo pisanje je veoma bitna veshtina za svakog potencijalnog security eksperta. Mozete ga shvatiti kao posebno napisan asm kod, ciji se krajnji oblik sastoji od char niza kod koga svaki bajt sadrzhi jednu hex vrednost(kasnije detaljnije objasnjeno). Primer takvog koda je shellcode cija je glavna svrha pokretanje shella(/bin/sh). Danas taj naziv nosi svaki oblik bytecode-a. Isti termin cemo i mi koristiti ubuduce. Tekst ne obuhvata gradivo koje se tice same upotrebe shellcode i pisanja exploita. Iako je na pocetnickom nivou njegovo citanje zahteva neko predznanje sto ce se uociti kroz tekst. Izlozheni materijal odnosi se uglavnom na GNU/Linux. Koristicemo najcesce standardne alate koji dolaze uz vecinu distribucija. /////////////////////////////////////////////////////////////////////////// --==<[ 2. Asm basics \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Kao sto je pomenuto u uvodu, za pisanje shellcode neophodno je poznavanje programiranja u asembleru. U cilju da tekst prilagodim i onima koji to znanje ne poseduju u ovom poglavlju pokusacu da vas uputim u neke asm osnove, bar toliko da bi citanje nastavili sa razumevanjem. ---< 2.1 Registers IA-32 procesor radi sa nekoliko razlicitih registara. Za vas je je vazna njihova primena kod koriscenja sistemskih poziva, necemo ulaziti u detalje. Registri koje cemo mi koristiti i oni koji su vredni pomena su: eax, ebx, ecx, edx, esp i eip. Prva cetiri su registri opste namene i svi ovi registri su 32 bitni i poticu, tj. prosireni su(extended, prefix 'e'), od 16 bitnih iste namene ax, bx, cx i dx. Oni se mogu podeliti na 8 bitne gornje(high) i donje(low) a to su al i ah, bh i bl, ch i cl, dh i dl. eax - poznatiji je kao akumulator registar. Koristi se kod ulazno/izlaznih (I/O) i aritmetickih operacija kao i kod izvrshavanja poziva interupta. Koristicemo ga za cuvanje vrednosti tj. broja sistemskog poziva koji ce se izvrshavati(poglavlje 3 - Using sys calls) ebx - bazni(base) register. Posluzice nam za predavanje prvog argumenta sistemskog poziva. ecx - brojac(counter) register. Koristicemo ga za drugi argument. edx - data register. Treci argument. esp - pokazivac na stack(stack pointer). eip - pokazivac instrukcija(instruction pointer). Pokazuje na offset naredne instrukcije u text(code) segmentu. Prednost koriscenja manjih registara uochice se kod izbegavanja null byte-ova (pod-poglavlje 4.3). ---< 2.2 Instructions Kao i svaki programski jezik i asembler ima svoju sintaxu. Tacnije, ima dva glavna tipa, a to su AT&T i Intel sintaxa. U nasem tekstu cemo koristiti upravo Intel. Razlog za to je veca citljivost samog koda, sto je nama i potrebno. Neki disasembleri koriste AT&T sintaxu u svom outputu (gdb, objdump...) i zbog toga cemo pomenuti neke generalne razlike od Intel. Upoznajmo se sa nekim osnovnim instrukcijama: mov - Prenosenje vrednosti. Sintaxa: mov , gde destinacija odnosno izvoriste moze biti register, memorijska adresa ili konstanta(mov reg,reg ; mov reg,mem ; mov mem,reg ; mov reg,const mov mem, const). Memory-to-memory je vec nemoguce. Primer: mov eax, 70 ; cuva vrednost 70 u eax Velicine operanda bi trebalo da budu iste. Dakle nesto kao mov al, ebx nije validno(u 8 bita ne moze se smestiti 32). add - Dodaje/sabira vrednosti. Sintaxa: add ,. Vrednost izvorista dodaje na vrednost destinacije i rezultat cuva u destinaciji ili drugacije receno sabira ih. To mogu biti isto kao kod mov registar, memorijska adresa i konstanta. Primer: add eax, ebx ; vrednost iz ebx dodaje se na eax Ova akcija je ekvivalent ovome eax = ebx + eax. sub - Oduzimanje vrednosti. Sintaxa: sub , Isti slucaj kao kod add samo je u pitanju oduzimanje. Primer: sub eax, ebx ; eax = eax - ebx Takodje postoje div i mul(division i multiplication) tj. deljenje i mnozenje. push - Guranje vrednosti na stek. Sintaxa: push (reg, mem, const). Koristi se za smeshtanje privremenih podataka na stack (Ovde je potrebno malo razumevanja LIFO ogranizacije (Last In First Out) i uloge sp. Dakle, vrednost koja je zadnja gurnuta na stack je prva koja se skida sa njega. Pritom se posle svakog guranja dekrementira pokazivac na stack(sp). Primer: push eax ; gura sadrzhaj eax na stack pop - Skidanje sa stack. Sintaxa: pop (reg, mem). U ovom slucaju se sp inkrementira. Primer: pop eax ; eax dobija vrednost sa vrha stacka jmp - Bezuslovni skok. Menja EIP(pokazivac instrukcija) na odredjenu adresu. Sintaxa: jmp