Disabling stack protection in GCC not working

2020-07-22 04:18发布

问题:

I'm trying to recreate a stack buffer overflow using the classic overflow with strcpy using this function:

#include <stdio.h>
#include <string.h>
void main(int argc, char **argv) {
    char buf[100];
    strcpy(buf,argv[1]);
    printf("Done!\n");
}

I have tried compiling with all the various flags in order to remove the stack protection gcc -o vuln vuln.c -fno-stack-protector -g -z execstack as well as removing ASLR with sudo echo 0 > /proc/sys/kernel/randomize_va_space.

I can get my nop-shellcode-address to overwrite the saved EIP but it crashes on the RET. I figured out to disable SIGSEGV in gdb with HANDLE SIGSEGV ignore etc. however running outside GDB I continue to get Segmentation Fault regardless of where I set the return address to.

Any thoughts besides getting an older version of gcc? Currently using gcc version 6.1.1.

回答1:

Linux follows the W^X principle: it marks memory pages as non-executable unless they are part of the code section. This goes beyond the scope of your compiled application, and for good reason. The OS assumes this responsibility to defend against buffer overflow attacks from any program executed on the system; especially programs that are actively attempting to perform a buffer overflow attacks, like yours.

You are attempting to write code on the stack via buf and overwrite the function's return address to jump execution into the newly injected code. When the function returns, the program counter is set to the overridden return address, which now points to stack memory. The SIGSEGV is thrown when the program counter attempts to execute the next instruction due to the revoked execute permissions on stack memory pages.

To execute from the stack, the OS stack protection must be disabled.

Fortunately Linux provides execstack for such a case to be used at the discretion of the user.

See this Unix & Linux Stack Exchange post for more general information.


Debugging Injected Data

If you are getting a SIGSEGV in gdb, it probably means that there is some error in your buffer overrun attempt. To avoid messing with the clean-up at the end of main, I would suggest making a function that you call from main to do your buffer overrun:

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

char injection_code[] = ""; // buffer overrun data here


void foo () {
    char buf[100];

    // memcpy used to copy the full injection string, including any nested 0s
    memcpy(buf, injection_code, 108); // +8 here to handle 64-bit system RAs
}

int main (int argc, char** argv) {
    foo();
    printf("Done!\n");
    return 0;
}

Use GDB to debug. I suggest putting a break point at the end of foo and checking the registers line up with what you'd expect with info registers. You'll probably be most interested in the instruction pointer, which you can check with info registers rip (64-bit) or info registers eip (32-bit).

You can explore what the stack looks like by using print or x/x in GDB. For instance, you can check $rbp+8 to see what the return address (RA) is on the stack.

GDB will SIGSEGV on the ret instruction if the RA points to an invalid location:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004005bc in foo () at bufferoverflow.c:27

You can check to see if it faulted on the ret instruction by checking the instruction pointer address at the time of fault (in the example above, it would be 0x00000000004005bc) against the program's assembly (use disassemble function_name in GDB).

If the return address points back in the stack, it may throw a SIGILL for an illegal instruction, which means your return address may not be aligned properly, or your injected instructions are is malformed:

Program received signal SIGILL, Illegal instruction.
0x00007fffffffdc13 in ?? ()

Again, you can use GDB to explore your stack to see why.

GDB is useful for getting the structure of the buffer overflow correct. Once it works as intended however, remember that memory addresses may be shifted when executed outside of GDB, especially if you compile without the debug flag (-g). You will have to play around with the return address a little bit to get it where you want it in a 'live' environment.


An Aside

In general, buffer overflow attacks that directly run injected code are generally not feasible with today's stack protection mechanisms. Normally there needs to be additional vulnerabilities present to be able to execute arbitrary code.



回答2:

I finally figured out the issue. GCC was aligning my instructions and variables to 16-byte boundaries. As such, it was adding assembly instructions i.e. lea ecx,[esp+0x4] etc. at the prologue and similar on the function epilogue which shifted the stack pointer around a bit and was messing around with the layout of the stack. I recompiled with -mpreferred-stack-boundary=2 which fixed the issue in GDB. Now I just need to play with those return addresses like you mentioned. Thanks!!