Stack Overflow Continued.

This time we are going to look at what happens when ASLR is left in place, and when we move to a 64 bit Debian distro.

ASLR

First a little bit of information about what Address Space Layout Randomization is.  ASLR is a method of making the memory locations of key pieces of memory unknown to an attacker by randomizing the assignment of memory addresses.  One piece of information that is randomized is the base of the stack.  We saw that last time as the value of the register ESP.

If you’re interested in learning more about ASLR, including some weaknesses, there is a pretty cool paper here.  The paper also has a nice paragraph or so about the difference between the memory layout we see in an operating systems textbook and what actually happens in a real setting.  For just a quick overview there’s always wikipedia here.  As always I would try to find something backing up wikipedia before taking it as true.

Lets Keep ASLR Active

Using the same simple program as last time lets see what happens when we leave ASLR active on a Debian 32-bit distro. Just as a reminder here’s our program from Hacking The Art of Exploitation.

//A program to demonstrate a buffer overflow.
#include 
#include 

int main(int argc, char *argv[])
{
    int value = 5;
    char buffer_one[8], buffer_two[8];

    strcpy(buffer_one, "one"); //Put "one" in buffer one
    strcpy(buffer_two, "two"); //Put "two" in buffer two

    printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
    printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
    printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value);

    printf("\n[STRCPY] copying %d bytes into buffer_two\n\n", strlen(argv[1]));
    strcpy(buffer_two, argv[1]); //copy first command line argument into buffer two.

    printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
    printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
    printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value);
    
}

We set it up in gdb again so we can examine the memory addresses.  I will skip showing that again and we can just examine what the program is doing. The first run I will input 01234567890 as a command line argument, which will overflow into the buffer_one memory but won’t cause any catastrophic effects.


(gdb) break 12
Breakpoint 1 at 0x8048488: file overflow_example.c, line 12.
(gdb) break 17
Breakpoint 2 at 0x80484d1: file overflow_example.c, line 17.
(gdb) run 01234567890
Starting program: /home/exploit/Hacking/Exploits/overflow_example 01234567890

Breakpoint 1, main (argc=2, argv=0xbffff324) at overflow_example.c:13
13          printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
(gdb) i r esp ebp eip
esp            0xbffff250       0xbffff250
ebp            0xbffff278       0xbffff278
eip            0x8048488        0x8048488 <main+45>

Here’s the first stage with a break point right after we initialize the stack. This is our baseline for running the program. A second run inputting the character “A” 30 times has the following result.

(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: overflow_example AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, main (argc=2, argv=0xbffff314) at overflow_example.c:13
13          printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
(gdb) i r esp ebp eip
esp            0xbffff240       0xbffff240
ebp            0xbffff268       0xbffff268
eip            0x8048488        0x8048488 <main+45>
(gdb) x/16xw $esp
0xbffff240:     0xbffff4a1      0x0000002f      0x08049930      0x006f7774
0xbffff250:     0x00000002      0x00656e6f      0xbffff320      0x00000005
0xbffff260:     0xbffff280      0xb7fbd000      0x00000000      0xb7e2da63
0xbffff270:     0x08048570      0x00000000      0x00000000      0xb7e2da63
(gdb) cont
Continuing.
[BEFORE] buffer_two is at 0xbffff24c and contains 'two'
[BEFORE] buffer_one is at 0xbffff254 and contains 'one'
[BEFORE] value is at 0xbffff25c and is 5 (0x00000005)

[STRCPY] copying 30 bytes into buffer_two


Breakpoint 2, main (
    argc=, 
    argv=)
    at overflow_example.c:20
20          printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
(gdb) i r esp ebp eip
esp            0xbffff240       0xbffff240
ebp            0xbffff268       0xbffff268
eip            0x804850e        0x804850e <main+179>
(gdb) x/16xw $esp
0xbffff240:     0xbffff4a1      0x0000002f      0x08049930      0x41414141
0xbffff250:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff260:     0x41414141      0x41414141      0x00004141      0xb7e2da63
0xbffff270:     0x08048570      0x00000000      0x00000000      0xb7e2da63

This is essentially the exact same outcome as last time. So leaving ASLR enabled hasn’t had any effect on this program in our 32-bit Debian. We are even getting the exact same stack base locations as the other day. Why would that be the case? We will continue to explore what occurs in the system as we work with more complicated programs and attacks. I like this example because it’s a nice reminder that just having something on your system doesn’t mean that it works in the way you may intuitively think it does.  That first naive notion that ASLR randomizes the location of the stack base so every time we run our program we will get a new memory address for esp is mistaken.  As we will continue to learn just because a protection exists doesn’t mean it mitigates everything, or is configured correctly.

What about x86_64?

Lets move to a 64-bit machine.  To keep as much the same as possible I went ahead and set up a Debian 64 environment.  There are also a few things to point out. The address space of a 64-bit machine is larger than the address space of a 32-bit machine, so the addresses in memory will be larger. The registers are also named differently. So we will look at rsp instead of esp.

I am leaving ASLR enabled to see if we get a change in the stack pointer in this one.  Here’s the first run:

(gdb) run 01234567890
Starting program: overflow_example 01234567890

Breakpoint 1, main (argc=2, argv=0x7fffffffe3b8) at overflow_example.c:12
12	    printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
(gdb) i r rsp rbp rip
rsp            0x7fffffffe2a0	0x7fffffffe2a0
rbp            0x7fffffffe2d0	0x7fffffffe2d0
rip            0x4005c0	0x4005c0 <main+42>

We can see the larger address locations plainly here. When we look into our memory we also notice a difference.


(gdb) x/16xw $rsp
0x7fffffffe2a0:	0xffffe3b8	0x00007fff	0x00000000	0x00000002
0x7fffffffe2b0:	0x006f7774(2)	0x00000000	0x004004a0	0x00000000
0x7fffffffe2c0:	0x00656e6f(3)	0x00007fff	0x00000000	0x00000005 (1)
0x7fffffffe2d0:	0x00000000	0x00000000	0xf7a52b45	0x00007fff

Notice that value here stands out at us again at (1). But we also notice that the beginning of buffer_two is located in a different place. Similarly we have a different location for buffer_one (3) as well. So the layout of the stack is different than our experience on the 32 bit machine. There are 16 bytes between buffer_two and buffer_one as well. Lets continue with the overflow

(gdb) i r rsp rbp rip
rsp            0x7fffffffe2a0	0x7fffffffe2a0
rbp            0x7fffffffe2d0	0x7fffffffe2d0
rip            0x40064f	0x40064f <main+185>
0x7fffffffe2a0:	0xffffe3b8	0x00007fff	0x00000000	0x00000002
0x7fffffffe2b0:	0x33323130	0x37363534	0x00303938	0x00000000
0x7fffffffe2c0:	0x00656e6f	0x00007fff	0x00000000	0x00000005
0x7fffffffe2d0:	0x00000000	0x00000000	0xf7a52b45	0x00007fff
(gdb) print buffer_one
$3 = "one\000\377\177\000"
(gdb) print buffer_two
$4 = "01234567"

Ok so there’s a big difference here. The program took the command line argument and chopped off the excess in this case. So we have some protection in the 64-bit version of Debian. When we continue we get an interesting output though.

(gdb) cont
Continuing.
[AFTER] buffer_two is at 0x7fffffffe2b0 and contains '01234567890'
[AFTER] buffer_one is at 0x7fffffffe2c0 and contains 'one'
[AFTER] value is at 0x7fffffffe2cc and is 5 (0x000000005)
[Inferior 1 (process 5210) exited with code 064]

We see here that our program behavior is actually handled in a fairly logical way. The entire string was placed in buffer_two and buffer_one was protected from the overflow. Lets try it again with the larger overflow input.


(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: overflow_example AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, main (argc=2, argv=0x7fffffffe398) at overflow_example.c:12
12	    printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
(gdb) i r rsp rbp rip
rsp            0x7fffffffe280	0x7fffffffe280
rbp            0x7fffffffe2b0	0x7fffffffe2b0
rip            0x4005c0	0x4005c0 <main+42>

We have that the stack base pointer has changed. However, if you recall the stack started growing as we increased the size of the overflow in our 32-bit machine. Our stack base has decreased by 32 bytes. This is similar to the 32-bit case. There is something we haven’t really discussed that has to be stored in the program information as well. Which may indeed explain what is causing our stack to grow when we increase the data for the overflow. I will leave that discussion for later and focus on observing what happens here.


(gdb) x/16xw $rsp
0x7fffffffe280:	0xffffe398	0x00007fff	0x00000000	0x00000002
0x7fffffffe290:	0x006f7774	0x00000000	0x004004a0	0x00000000
0x7fffffffe2a0:	0x00656e6f	0x00007fff	0x00000000	0x00000005
0x7fffffffe2b0:	0x00000000	0x00000000	0xf7a52b45	0x00007fff
(gdb) cont
Continuing.
[BEFORE] buffer_two is at 0x7fffffffe290 and contains 'two'
[BEFORE] buffer_one is at 0x7fffffffe2a0 and contains 'one'
[BEFORE] value is at 0x7fffffffe2ac and is 5 (0x00000005)

[STRCPY] copying 30 bytes into buffer_two


Breakpoint 2, main (argc=2, argv=0x7fffffffe398) at overflow_example.c:19
19	
(gdb) i r rsp rbp rip
rsp            0x7fffffffe280	0x7fffffffe280
rbp            0x7fffffffe2b0	0x7fffffffe2b0
rip            0x40064f	0x40064f <main+185>
(gdb) x/16xw $rsp
0x7fffffffe280:	0xffffe398	0x00007fff	0x00000000	0x00000002
0x7fffffffe290:	0x41414141	0x41414141	0x41414141	0x41414141
0x7fffffffe2a0:	0x41414141	0x41414141	0x41414141	0x00004141
0x7fffffffe2b0:	0x00000000	0x00000000	0xf7a52b45	0x00007fff
(gdb) cont
Continuing.
[AFTER] buffer_two is at 0x7fffffffe290 and contains 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
[AFTER] buffer_one is at 0x7fffffffe2a0 and contains 'AAAAAAAAAAAAAA'
[AFTER] value is at 0x7fffffffe2ac and is 16705 (0x00004141)
[Inferior 1 (process 5301) exited with code 070]
(gdb) 

We ended up with the same effect of overwriting value again. However we didn’t overwrite nearly as far into the memory as we did on the 32 bit machine. We had to basically double the overflow to reach the value variable stored in the stack.

Conclusion.

We can see that just because ASLR is set up on a machine it is not as simple as having a random stack base location assigned to the program each run.  Each time the program was run on the 32 bit machine and the 64 bit machine the stack base was in the same location if the input was the same.  Which lead to the hint about why the stack base was moving in some of the examples.

The 64-bit machine still allowed us to overwrite the buffers in our program, but we needed more data to do it.  The location of the buffers were also changed relative to each other in the stack.  So there is certainly some information we need to go over before we are running the exploits on a 64 bit machine and understanding everything that is happening.

Next time I will go over another buffer overflow program that will give a simple example of using the available overflow to have an impact on program behavior.

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