9

I am learning about buffer overflows and am trying to make one. I have this code:

#include <stdio.h>

char *secret = "password";

void go_shell() {
    char *shell =  "/bin/sh";
    char *cmd[] = { "/bin/sh", 0 };
    setreuid(0);
    execve(shell,cmd,0);
}

int authorize() {
    char password[64];
    printf("Enter Password: ");
    gets(password);
    if (!strcmp(password,secret)) {
        return 1;
    }
    else {
        return 0;
    }
}

int main() {
    if (authorize()) {
        printf("login successful\n");
        go_shell();
    } else {
        printf("Incorrect password\n");
    }
    return 0;
}

I compile this with gcc and then run it in gdb

I enter about 100 "A"s as the password and the program crashes.

The problem is no register is overwritten to 0x4141414141414141

I googled this and added the -fno-stack-protector flag to gcc, which allowed RBP to be overwritten to 0x4141414141414141 but nothing else.

I was wondering if there was a way to compile the code so that RIP can be overwritten.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
carloabelli
  • 4,289
  • 3
  • 43
  • 70
  • Are you using Linux? You also need to set some flag there, I think. – nhahtdh Feb 09 '13 at 15:58
  • Using Mac OSX. Any idea what the flag is? – carloabelli Feb 09 '13 at 15:59
  • I don't know about Mac OSX - probably going to be vastly different from Ubuntu (sorry, I actually did this on Ubuntu, not Linux). – nhahtdh Feb 09 '13 at 16:27
  • @nhahtdh Ubuntu is a Linux distribution. – Mahmoud Al-Qudsi Feb 09 '13 at 19:31
  • 6
    Buffer overflows won't generally overwrite register variables at all. If you overwrite the stack, the return address might be overwritten, and maybe the frame pointer too if you've got frame pointers (no `-fomit-frame-pointer`). So those might be pulled back into registers, but that's because the stack was overwritten first, not because the registers were overwritten directly. – Jonathan Leffler Feb 09 '13 at 19:32
  • @JonathanLeffler I know I have overwritten stack. The tutorial that I was using had RIP overwritten because the stack was overwritten. I did not get this, wondering how I could get this. – carloabelli Feb 09 '13 at 19:51
  • The crash almost certainly indicates your attempt to overflow the password[] buffer has succeeded. You've overwritten the stack frame for authorize(), so its return address has become garbage. Then when authorize() tries to return to that garbage address, the program crashes. – Bob Murphy Feb 09 '13 at 19:52
  • @cabellicar123 You talk about IP getting overwritten... You know IP is Instruction Pointer, meaning address where CPU is executing machine code? And function returning with ret instruction simply pops new value for that register from stack, hopefully one which was pushed there by earlier call. What makes you think this did not happen, and be reason for crash? – hyde Feb 09 '13 at 20:01
  • Another thing about buffer overflow is that endianess matters. We (by "we," I mean "I") tend to think of hexadecimal in big endian format. So working on (most?) x86 processors if I am thinking of `0xdeadbeef`, I need to use `0xefbedade` instead. – RageD Feb 09 '13 at 20:02
  • You *did* "overwrite" the RIP register. That's why it crashed. Clearly the value you wrote isn't good enough to make that execute the payload. – Hans Passant Feb 09 '13 at 20:02
  • The only thing then that I do not understand is why when I look at the registers after the crash that RIP is poing to `0x7fff8cacaa3b` some function called `_findenv` and not `0x4141414141414141` if it was popped off the stack containing `0x4141414141414141` – carloabelli Feb 09 '13 at 20:10

2 Answers2

3

Your code already does what you want if you compile with -fno-stack-protector. The reason you don't see RIP with a value of 0x4141414141414141 in GDB is that a general protection fault is thrown before RIP is updated. (If a page fault occurs, the GPF handler usually loads the page from swap and resumes execution by starting with the failed instruction.)

nwellnhof
  • 32,319
  • 7
  • 89
  • 113
  • Thanks for the help, but I was wondering if there was a way to turn off that GPF so that RIP could be set to `0x4141414141414141` because that is what happens in the tutorial I am using. – carloabelli Feb 11 '13 at 22:33
  • @cabellicar123 You can't avoid the GPF unless you create an executable page at that address. Just proceed with your tutorial. Probably the next step will be to supply a specially crafted password which make the code jump to a valid address. – nwellnhof Feb 11 '13 at 22:47
1

The reason you get a crash of EIP 0×41414141 on x32 is because when the program pops the previously saved EIP value off the stack and back into EIP the CPU then tries to execute the instruction at memory address 0×41414141 which causes a segfault. (it must fetch the page prior to execution of course)

Now, during x64 execution when the program pops the previously saved RIP value back into the RIP register the kernel then tries to execute the instructions at memory address 0×4141414141414141. Firstly, due to canonical form addressing, bits 48 through 63 of any virtual address must be copies of bit 47 (in a manner akin to sign extension), or the processor will raise an exception. If that was not an issue- the kernel does additional checks before invoking the page fault handler since the max user space address is 0x00007FFFFFFFFFF.

To recap, in x32 architecture the address is passed without any “validation” to the page fault handler which attempts to load the page which triggers the kernel to send the program segfault but x64 does not get this far.

Test it, overwrite RIP with 0×0000414141414141 and you will see the expected value is placed in RIP since the prechecks by the kernel pass and then the the page fault handler is invoked like the x32 case (which of course, then causes the program to crash).