====== Stack Protection ====== Modern compilers provide stack protection mechanism that can be compiled into a binary. Looking at GCC, it provides different flags to enable stack protection for functions. Using the ''-fstack-protector'' flag during compilation makes GCC supply every function with a buffer of at least 8 bytes. ''-fstack-protector-strong'' or ''-fstack-protector-all'' can be used to weaken this constraint and protect more or even all functions. Manual selection of the protected function is possible with the ''-fstack-protector-explicit'' flag(([[http://man7.org/linux/man-pages/man1/gcc.1.html|gcc(1) - Linux manual page]])). Refer to the next table for details about the compiler flags. ^ Flag ^ Protected functions ^ | ''-fstack-protector'' | Functions with arrays of at least 8 bytes | | ''-fstack-protector-strong'' | Functions meeting any of the following criteria(([[https://gcc.gnu.org/ml/gcc-patches/2012-06/msg00974.html|[PATCH] Add a new option "-fstack-protector-strong" (patch / doc inside)]])):\\ * The function uses a local variable’s address in the right-hand side of an assignment\\ * The function passes a local variable's address to a function\\ * The function declares an array (regardless of its length\\ * The function uses register local variables | | ''-fstack-protector-explicit'' | All functions with the "stack_protect" attribute | | ''-fstack-protector-all'' | All functions | Below follows a description of GCC's stack protection features. ===== Stack Canaries ===== Right after the stack frame is created, a protection value is pushed to the stack. There are several schemes to generate this value((T. Saito, R. Watanabe, S. Kondo, S. Sugawara and M. Yokoyama, "A Survey of Prevention/Mitigation against Memory Corruption Attacks," 2016 19th International Conference on Network-Based Information Systems (NBiS), Ostrava, 2016, pp. 500-505.)), e.g. GCC uses a random value provided by the Linux kernel(([[https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/asm/stackprotector.h|Linux kernel - stackprotector.h]])). This so-called "stack canary" is located between the local variables and the meta information on the stack. If no overflow occurred during the execution of the function, the value of the stack canary is unchanged. In case the value changed, execution is aborted. {{.stack_protector.png?350|}} The behavior of the stack protector is tested with a simple ''strcpy()'' overflow. // gcc -g -O0 -m32 -fstack-protector canary.c #include #include int main(int argc, char *argv[]) { char buffer[8] = {0}; if(argc != 2) { printf("A single argument is required.\n"); return 1; } strcpy(buffer, argv[1]); return 0; } Trying to overflow the buffer causes the execution to terminate. $ ./a.out AAAAAAAAAAAAAAAAAAAAAAAA *** stack smashing detected ***: terminated [1] 13372 abort (core dumped) ./a.out AAAAAAAAAAAAAAAAAAAAAAAA Disassembling the generated binary with ''objdump'' reveals implementation details of the GCC stack protector. First, a random value is read from ''gs:0x14''. This is a special read-only memory region providing a fixed random number per execution which is saved on the stack. 5d1: 65 8b 1d 14 00 00 00 mov ebx,DWORD PTR gs:0x14 5d8: 89 5d f4 mov DWORD PTR [ebp-0xc],ebx After the actual function body, the random value is read again and compared to the saved one. If the values are not equal, an error is raised. 62d: 65 33 0d 14 00 00 00 xor ecx,DWORD PTR gs:0x14 634: 74 05 je 63b 636: e8 85 00 00 00 call 6c0 <__stack_chk_fail_local> As the canary is located between local variables and the meta information on the stack, overflows targeting the variables can not be detected by this mechanism. ===== Parameter Copying ===== Right after the space for local variables is allocated, the parameters of the function are copied to local variables at the top of the stack((Gerardo Richarte, "Four different tricks to bypass StackShield and StackGuard protection", 2002)). Applying this procedure protects the parameters from overflows in local variables. In general, the stack layout after copying the parameters is structured as pictured. {{.stack_parameter_copying.png?400|}} The following example code is used to further illustrate the concept. // gcc -g -O0 -m32 -std=c99 -fstack-protector copying.c #include #include void user_stuff(char buffer[], int is_admin); int main(int argc, char *argv[]) { char buffer[8] = {0}; int is_admin = 0; printf("Enter name:\n"); gets(buffer); if(strcmp(buffer, "admin") == 0) is_admin = 1; user_stuff(buffer, is_admin); return 0; } void user_stuff(char buffer[8], int is_admin) { if(is_admin) printf("User %s is an administrator\n", buffer); else printf("User %s is not an administrator\n", buffer); } Disassembling the compiled binary shows the following assembler code. 6dc: 8b 55 08 mov edx,DWORD PTR [ebp+0x8] 6df: 89 55 e4 mov DWORD PTR [ebp-0x1c],edx 6e2: 8b 55 0c mov edx,DWORD PTR [ebp+0xc] 6e5: 89 55 e0 mov DWORD PTR [ebp-0x20],edx These instructions take the parameters and copy them to local variables. Note that the parameter copying happens before the variable reordering described in the following section. ===== Variable Reordering ===== The usage of stack canaries secures the meta information located on the stack. However, there is still the possibility to overflow from an array into other local variables. As protecting every single parameter would be quite cumbersome and inefficient, modern compilers like the GCC reorder variables to protect them from being overwritten. This can be achieved by simply locating arrays below (at higher memory addresses) other local variables((Robert C. Seacord (2013). Secure Coding in C and C++ (2nd edition)))((Gerardo Richarte, "Four different tricks to bypass StackShield and StackGuard protection", 2002)). An example of an original stack layout before variable reordering is shown below. {{.stack_variable_reordering_before.png?400|}} Clearly, buffers with a number from 2 to n may overflow variable 1. Buffer 1 can also overflow all other local variables. After the compiler separates buffers from simple variables the example stack frame looks as follows. {{.stack_variable_reordering_after.png?400|}} To observe this behavior, the code below prints the addresses of the local variables. // gcc -g -O0 -m32 -fstack-protector reordering.c #include int main() { int a; char b[4]; int c; char d[8]; int e; char f[6]; printf("a: %p\n", &a); printf("b: %p\n", b); printf("c: %p\n", &c); printf("d: %p\n", d); printf("e: %p\n", &e); printf("f: %p\n", f); return 0; } Due to [[.aslr|ASLR]] the output of the program is not constant, but the order of the variable does not change between multiple runs. $ ./a.out a: 0xffa08a7c b: 0xffa08a8a c: 0xffa08a80 d: 0xffa08a94 e: 0xffa08a84 f: 0xffa08a8e GCC arranges simple variables above buffers. Additionally, buffers are ordered ascending by their size, locating larger buffers at higher memory addresses. \\ ----
[[.ascii-armor|← Back to ASCII-armored addresses]] [[..start|Overview]] [[.aslr|Continue with Address Space Layout Randomization (ASLR) →]]