Skip to content
Advertisement

Modify the return address of a C function with buffer overflow vulnerability

I am trying to modify the following C program so that the main function will skip the printf(“x is 1”) line and only print “x is 0”.

 void func(char *str) {
         char buffer[24];
         int *ret;

         ret = buffer + 28; // Supposed to set ret to the return address of func
         (*ret) += 32; // Add the offset needed so that func will skip over printf("x is 1")
         strcpy(buffer, str);
 }

 int main(int argc, char **argv) {
         int x;
         x = 0;
         func(argv[1]);
         x = 1;
         printf("x is 1");
         printf("x is 0");
         getchar();
 }

As the comments imply, the ret pointer needs to first be set to the return address of the function. I then need to add on an offset that will push it over the line I want to skip. I am running this code on a Linux system with 2 x Intel(R) Xeon(TM) CPU 3.20GHz. I am using gcc version 4.7.2 (Debian 4.7.2-5) to compile. I’m also trying to use example3.c from this (http://insecure.org/stf/smashstack.html) link for reference. Here is a disassembly of the main function using gdb:

Dump of assembler code for function main:
    0x0000000000400641 <+0>:     push   %rbp
    0x0000000000400642 <+1>:     mov    %rsp,%rbp
    0x0000000000400645 <+4>:     sub    $0x20,%rsp
    0x0000000000400649 <+8>:     mov    %edi,-0x14(%rbp)
    0x000000000040064c <+11>:    mov    %rsi,-0x20(%rbp)
    0x0000000000400650 <+15>:    movl   $0x0,-0x4(%rbp)
    0x0000000000400657 <+22>:    mov    -0x20(%rbp),%rax
    0x000000000040065b <+26>:    add    $0x8,%rax
    0x000000000040065f <+30>:    mov    (%rax),%rax
    0x0000000000400662 <+33>:    mov    %rax,%rdi
    0x0000000000400665 <+36>:    callq  0x4005ac <func>
    0x000000000040066a <+41>:    movl   $0x1,-0x4(%rbp)
    0x0000000000400671 <+48>:    mov    $0x40075b,%edi
    0x0000000000400676 <+53>:    mov    $0x0,%eax
    0x000000000040067b <+58>:    callq  0x400470 <printf@plt>
    0x0000000000400680 <+63>:    mov    $0x400762,%edi
    0x0000000000400685 <+68>:    mov    $0x0,%eax
    0x000000000040068a <+73>:    callq  0x400470 <printf@plt>
    0x000000000040068f <+78>:    callq  0x400490 <getchar@plt>
    0x0000000000400694 <+83>:    leaveq
    0x0000000000400695 <+84>:    retq
 End of assembler dump.

Using what I’ve read from the example, my buffer is 24 bytes long and I should add an extra 4 bytes for the SFP size. This would mean I add 28 bytes to get to the return address of <+41>. It then looks like I want to jump to the last printf call at <+73>. This should be an offset of 32. However, when I execute the code, “x is 1” is still printed. I can’t seem to find out why. Is there something wrong with my math or assumptions?

Advertisement

Answer

You should disassemble the function func() too in order to have a better idea how things are going on. Moreover, I did not understood the role of your call to strcpy(), just a cause of segmentation error for me I commented it out to make your code work.

Do not forget that the sizes you can see in the code are printed in hexadecimal, while you input the buffer shifts in your code in decimal. So, when you read something as:

mov    %rdi,-0x28(%rbp)

You must think of 40 bytes (0x28 hexa = 40 decimal), and NOT 28 bytes.

The code above is actually extracted from the func() function disassembly. As @cybermike mentioned, do not forget that while Alpeh1’s text remains a reference on the subject, it is getting quite old now: architecture and protection systems have widely evolved until now.

As mentioned here, on x64 architectures the compiler will now try to align the stack addresses with 16 bytes boundaries, so to allocate the size for your 24 chars array it will actually reserve 32 bytes, ie. the nearest boundary.

Add to that the 8 bytes allocated for your “rest” pointer, then you know that your return address is located exactly 40 bytes away.

Then, looking at main() disassembly, the normal return address is:

0x00000000004005fe <+41>:    movl   $0x1,-0x4(%rbp)

And we want it to be:

0x0000000000400614 <+63>:    mov    $0x4006bb,%edi

So we have to increase the return by 63 – 41 = 22.

So, to summarize, I have your exercise working as expected with the following func() function:

void func(char *str) {
    char buffer[24];
    int *ret;

    ret = buffer + 40; // Supposed to set ret to the return address of func
    (*ret) += 22; // Add the offset needed so that func will skip over printf("x is 1")
    //strcpy(buffer, str);

}

Execution result:

$ ./se 
x is 0
Advertisement