A walk outside the sandbox

Home Blog Cheat Sheets MacOS Tips Area 51 About

Test Stack Smashing Protection



In these four short posts we’ll test a few traditional anti-exploitation measures. The experiments below are inspired from the great book The Mac Hacker’s Handbook and are done on a macOS Sierra.

To check the other tests, see the links below:

Stack smashing protection

Start with this typical buffer overflow example:

int main(int argc, char *argv[]){
    char buf[16];
    strcpy(buf, argv[1]);

Disabling stack smashing protection is a bit tricky. Let’s try the traditional gcc option -fno-stack-protector and see what happens:

$ lldb ./stack_police $(python -c 'print "A" * 30')
(lldb) target create "./stack_police"
Current executable set to './stack_police' (x86_64).

(lldb) run
Process 5428 launched: './stack_police' (x86_64)
2018-03-21 10:55:18.812671+0000 stack_police[5428:247259] detected buffer overflow
Process 5428 stopped
* thread #1, queue = '', stop reason = signal SIGABRT
    frame #0: 0x00007fffe3402d42 libsystem_kernel.dylib`__pthread_kill + 10
->  0x7fffe3402d42 <+10>: jae    0x7fffe3402d4c            ; <+20>
    0x7fffe3402d44 <+12>: movq   %rax, %rdi
    0x7fffe3402d47 <+15>: jmp    0x7fffe33fbcaf            ; cerror_nocancel
    0x7fffe3402d4c <+20>: retq
Target 0: (stack_police) stopped.

So a buffer overflow is still detected. To understand why, just step through the short code of main function, until we see this:

(lldb) breakpoint set --name main
Breakpoint 2: where = stack_police`main, address = 0x0000000100000f50

(lldb) run
There is a running process, kill it and restart?: [Y/n] y
Process 5437 exited with status = 9 (0x00000009)
Process 5442 launched: './stack_police' (x86_64)
Process 5442 stopped
* thread #1, queue = '', stop reason = breakpoint 2.1
    frame #0: 0x0000000100000f50 stack_police`main
->  0x100000f50 <+0>: pushq  %rbp
    0x100000f51 <+1>: movq   %rsp, %rbp
    0x100000f54 <+4>: subq   $0x30, %rsp
    0x100000f58 <+8>: movl   $0x10, %eax
Target 0: (stack_police) stopped.

->  0x100000f6e <+30>: movq   0x8(%rsi), %rsi
    0x100000f72 <+34>: movq   %rcx, %rdi
    0x100000f75 <+37>: callq  0x100000f8a               ; symbol stub for: __strcpy_chk
    0x100000f7a <+42>: xorl   %r8d, %r8d

Which leads us to the __strcpy_chk function, which is basically the same as strcpy but with buffer overflow checking.

A bit of online research reveals that this additional check was added by the FORTIFY_SOURCE flag enabled by default, so in order to compile a vulnerable program to play with, we would need the following options:

$ gcc  -fno-stack-protector -D_FORTIFY_SOURCE=0 stack_police.c -o stack_police
$ otool -Iv stack_police | grep _chk


By default, both the stack smashing protection and FORTIFY_SOURCE are enabled. With FORTIFY_SOURCE, GCC uses replacement functions for strcpy, memcpy and memset which account for the destination buffer length, in order to prevents buffer overflow attacks:

$ gcc  stack_police.c -o stack_police

$ otool -Iv stack_police | grep _chk
0x0000000100000f80     2 ___stack_chk_fail
0x0000000100000f86     4 ___strcpy_chk
0x0000000100001010     3 ___stack_chk_guard
0x0000000100001018     2 ___stack_chk_fail
0x0000000100001020     4 ___strcpy_chk