Stack Smashing Code Not Working on Linux Kernel 2.6.38.7... Please Help

Isnt Segmentation fault the same as the smashing the stack?

Briefly, no. Segmentation faults are when the kernel is able to detect an invalid memory access and then kills the process. Some invalid memory accesses cannot be detected by the kernel, and stack overflows are built on these. However, stack overflows can be detected by the compiler and are in recent versions of gcc (4.1+), which have built-in protection against stack smashing attacks. Basically, a "canary" value is placed on the stack between stack frames. There are checks to make sure the canary still has the correct value; if it doesn't (because it was overwritten and the overwriter couldn't guess the correct value) then the stack smashing protection routines execute. For more information, see:
http://en.wikipedia.org/wiki/Buffer_overflow_protection#GCC_Stack-Smashing_Protector_.28ProPolice.29
and
http://wiki.osdev.org/GCC_Stack_Smashing_Protector

You can disable the gcc protection with "-fno-stack-protector", for more on this see: Stack smashing code not working on Linux kernel 2.6.38.7... Please help

Conversely, a segmentation fault is just an invalid memory access that happens anywhere in the program, meaning the kernel detects access to memory that is not in the program's allowed memory region. AFAIK this is checked using a combination of x86 segments and virtual memory. There's no real way for the kernel/OS to know whether an access was in the original program code or the code was exploited somehow; either way, the program is attempting to access memory it cannot and so it is forcibly terminated.

Disabling stack protection in GCC not working

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.

Stack smashing detected

Stack Smashing here is actually caused due to a protection mechanism used by gcc to detect buffer overflow errors. For example in the following snippet:

#include <stdio.h>

void func()
{
char array[10];
gets(array);
}

int main(int argc, char **argv)
{
func();
}

The compiler, (in this case gcc) adds protection variables (called canaries) which have known values. An input string of size greater than 10 causes corruption of this variable resulting in SIGABRT to terminate the program.

To get some insight, you can try disabling this protection of gcc using option -fno-stack-protector while compiling. In that case you will get a different error, most likely a segmentation fault as you are trying to access an illegal memory location. Note that -fstack-protector should always be turned on for release builds as it is a security feature.

You can get some information about the point of overflow by running the program with a debugger. Valgrind doesn't work well with stack-related errors, but like a debugger, it may help you pin-point the location and reason for the crash.



Related Topics



Leave a reply



Submit