Examining The Stack By Varying Parameters or RTFM Your Choice.

Last time we looked at how the stacks (32-bit and 64-bit) filled up with our command line arguments in an overflow example from Hacking The Art of Exploitation.  We found out that there were 8 bytes between buffer_two and buffer_one along with four bytes between buffer_one and variable on the 64-bit stack.  Those numbers are important to know if we are going to try and overflow this program.  I want to know what happens when we vary the size of the buffers.

Changing Sizes:

I’m going to assume you remember the program, even if you don’t everything is the same except the size of the buffers.

//A program that gives an example of a basic buffer overflow
#include 
#include 

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

  int value = 5;
  char buffer_one[16], buffer_two[16];

  strcpy(buffer_one, "one");
  strcpy(buffer_two, "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("[STRCPY] copying %d bytes into buffer_two\n\n", strlen(argv[1]));
  strcpy(buffer_two, argv[1]);

  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);



}

There’s the code with the modified buffers. Lets do what we did last time and take a look at where we end up. If you remember we are looking for the ASCII representations of “two” and “one” to mark the base of each buffer. Lets take a look.

(gdb) x/32xw $rsp
0x7fffffffe030:	0xffffe168	0x00007fff	0x00000000	0x00000002
0x7fffffffe040:	0x00000001	0x00000000	0x004007ad	0x00000005
0x7fffffffe050:	0x00656e6f	0x00000000	0x00000000	0x00000000
0x7fffffffe060:	0x006f7774	0x00000000	0x00400530	0x00000000
0x7fffffffe070:	0xffffe160	0x00007fff	0xdf9a6600	0x0efa6269
0x7fffffffe080:	0x00400760	0x00000000	0xf7a2e830	0x00007fff
0x7fffffffe090:	0x00000000	0x00000000	0xffffe168	0x00007fff
0x7fffffffe0a0:	0xf7ffcca0	0x00000002	0x00400626	0x00000000

Well that was unexpected. We should be scratching our heads right about now. We haven’t consulted any documentation and we just expanded the buffers by 8-bytes.  Take a look at the bold memory.  Lets compare that with what happened with the first time we ran the program.

(gdb) x/32xw $rsp
0x7fffffffe060:	0xffffe178	0x00007fff	0x00000000	0x00000002
0x7fffffffe070:	0x006f7774	0x00000000	0x004004c0	0x00000000
0x7fffffffe080:	0x00656e6f	0x00007fff	0x00000000	0x00000005
0x7fffffffe090:	0x004006d0	0x00000000	0xf7a2e830	0x00007fff
0x7fffffffe0a0:	0x00000000	0x00000000	0xffffe178	0x00007fff
0x7fffffffe0b0:	0x00000000	0x00000002	0x004005b6	0x00000000
0x7fffffffe0c0:	0x00000000	0x00000000	0x6bd55c87	0xb652137c
0x7fffffffe0d0:	0x004004c0	0x00000000	0xffffe170	0x00007fff

We have flipped the buffer allocation in memory.  Can we even overflow value now?  (hint: that doesn’t look good for a positive answer because value is now in memory below the buffers.)   Lets see what happens when we run the program with varying entries.

(gdb) x/32xw $rsp
0x7fffffffe030:	0xffffe168	0x00007fff	0x00000000	0x00000002
0x7fffffffe040:	0x00000001	0x00000000	0x004007ad	0x00000005
0x7fffffffe050:	0x00656e6f	0x00000000	0x00000000	0x00000000
0x7fffffffe060:	0x41414141	0x41414141	0x41414141	0x41414141
0x7fffffffe070:	0xffffe100	0x00007fff	0xdf9a6600	0x0efa6269
0x7fffffffe080:	0x00400760	0x00000000	0xf7a2e830	0x00007fff
0x7fffffffe090:	0x00000000	0x00000000	0xffffe168	0x00007fff
0x7fffffffe0a0:	0xf7ffcca0	0x00000002	0x00400626	0x00000000

There’s our 16 bytes of A’s placed into buffer two.  Remember that the stack grows down to lower memory addresses, but our buffers are allocated up towards the higher memory addresses.  So the answer to our question before is a resounding no, we are not going to be overflowing value from buffer two.  Let’s see what happens when we overflow buffer_two with an input string of 20.

0x7fffffffe030:	0xffffe168	0x00007fff	0x00000000	0x00000002
0x7fffffffe040:	0x00000001	0x00000000	0x004007ad	0x00000005
0x7fffffffe050:	0x00656e6f	0x00000000	0x00000000	0x00000000
0x7fffffffe060:	0x41414141	0x41414141	0x41414141	0x41414141
0x7fffffffe070:	0x41414141	0x00007f00	0x13267f00	0xaf47f44f
0x7fffffffe080:	0x00400760	0x00000000	0xf7a2e830	0x00007fff
0x7fffffffe090:	0x00000000	0x00000000	0xffffe168	0x00007fff
0x7fffffffe0a0:	0xf7ffcca0	0x00000002	0x00400626	0x00000000

We overflowed into whatever followed buffer_two in the stack.  Which we can’t be sure of what that was at this point because we haven’t done more than just experimenting.  Lets see what happens when we switch the allocation of the variables in the program.

Rearrange the Variables:

We have reordered the variables in the source code as follows:

char buffer_two[16], buffer_one[16];
  int value = 5;

Lets see what happens in the stack based on that reordering.

0x7fffffffe030:	0xffffe168	0x00007fff	0x00000000	0x00000002
0x7fffffffe040:	0x00000001	0x00000000	0x004007ad	0x00000005
0x7fffffffe050:	0x006f7774	0x00000000	0x00000000	0x00000000
0x7fffffffe060:	0x00656e6f	0x00000000	0x00400530	0x00000000
0x7fffffffe070:	0xffffe160	0x00007fff	0x87dcc300	0x9eef9e56
0x7fffffffe080:	0x00400760	0x00000000	0xf7a2e830	0x00007fff
0x7fffffffe090:	0x00000000	0x00000000	0xffffe168	0x00007fff
0x7fffffffe0a0:	0xf7ffcca0	0x00000002	0x00400626	0x00000000

We can see that the allocation order of buffer_two and buffer_one has flipped their order in memory but value is still located below both of them.   From here we could overflow buffer_two into buffer_one, but we won’t be able to reach value.  Clearly something is going on here that we aren’t able to deduce just by looking at the memory.  We are going to have to look at some documentation or at least consult our very good friend Google.

Some Information:

Here are two short articles about the top of the stack that clearly makes at least one difference between the x86 and x86_64 stacks obvious.  They are really nicely written and they make one thing very obvious.  We are not going to experiment out the differences between the 32-bit and 64-bit stacks.  There is just too much going on to intuit it.

Conclusion:

There was a point to this post.   It just wasn’t the one that could be expected.  We aren’t going to be able to intuit the structure of the 64-bit x86_64 stack by throwing input strings at our program and watching what happens.

We aren’t able to see what’s happening enough by just looking at the program in memory alone.  I wrote this as a continuation of the theme from last time.  The point of this post is RTFM.  The second point is read what matters.  We don’t need to understand every single detail of the 64-bit stack to exploit a vulnerability that is well documented.  We just need to read the manual on the vulnerability. (Developing exploits is something else and there may not be a manual to read in that case, but we aren’t there yet.  We have a lot of manuals to read before we get there.)

Next time we will execute a buffer overflow on the 64-bit machine.  Until then I’m just going to leave this here from xkcd…..

rtfm

 

 

 

 

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