gcc security features (part 2)
(See part 1)
Remember: you must compile with -02 if you want the checks to be effective
DEB_BUILD_HARDENING_FORTIFY (gcc/g++ -D_FORTIFY_SOURCE=2)
The idea behind FORTIFY_SOURCE is relatively simple: there are cases where the compiler can know the size of a buffer (if it’s a fixed sized buffer on the stack, as in the example, or if the buffer just came from a malloc() function call). With a known buffer size, functions that operate on the buffer can make sure the buffer will not overflow.
Example:
void foo(char *string)
{
char buf[20];
strcpy(buf, string);
}
Execution will fail:
[home ~/harden] ./bad $(perl -e 'print "A"x100')
zsh: segmentation fault ./bad $(perl -e 'print "A"x100')
When compiling with -D_FORTIFY_SOURCE=2
, gcc will add some checks to
detect the overflow and terminate the program:
[home ~/harden] DEB_BUILD_HARDENING=1 make
[home ~/harden] ./bad $(perl -e 'print "A"x100')
*** buffer overflow detected ***: ./bad terminated
======= Backtrace: =========
/lib/libc.so.6(__fortify_fail+0x37)[0x2ba8d18fb787]
/lib/libc.so.6[0x2ba8d18f9e70]
./bad(main+0x26)[0x555555554856]
/lib/libc.so.6(__libc_start_main+0xf4)[0x2ba8d18411c4]
./bad[0x555555554789]
======= Memory map: ========
2ba8d1607000-2ba8d1622000 r-xp 00000000 03:01 468316 /lib/ld-2.7.so
2ba8d1622000-2ba8d1625000 rw-p 2ba8d1622000 00:00 0
2ba8d1821000-2ba8d1823000 rw-p 0001a000 03:01 468316 /lib/ld-2.7.so
2ba8d1823000-2ba8d1961000 r-xp 00000000 03:01 471074 /lib/libc-2.7.so
2ba8d1961000-2ba8d1b61000 ---p 0013e000 03:01 471074 /lib/libc-2.7.so
2ba8d1b61000-2ba8d1b64000 r--p 0013e000 03:01 471074 /lib/libc-2.7.so
2ba8d1b64000-2ba8d1b66000 rw-p 00141000 03:01 471074 /lib/libc-2.7.so
2ba8d1b66000-2ba8d1b6c000 rw-p 2ba8d1b66000 00:00 0
2ba8d1b6c000-2ba8d1b82000 r-xp 00000000 03:01 1045795 /lib/libgcc_s.so.1
2ba8d1b82000-2ba8d1d82000 ---p 00016000 03:01 1045795 /lib/libgcc_s.so.1
2ba8d1d82000-2ba8d1d83000 rw-p 00016000 03:01 1045795 /lib/libgcc_s.so.1
555555554000-555555555000 r-xp 00000000 03:07 1225 /home/pollux/harden/bad
555555754000-555555755000 r--p 00000000 03:07 1225 /home/pollux/harden/bad
555555755000-555555756000 rw-p 00001000 03:07 1225 /home/pollux/harden/bad
555555756000-555555777000 rw-p 555555756000 00:00 0 [heap]
7fffd948e000-7fffd94a3000 rw-p 7ffffffea000 00:00 0 [stack]
7fffd95fe000-7fffd9600000 r-xp 7fffd95fe000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
zsh: abort ./bad $(perl -e 'print "A"x100')
The difference is that there is no segmentation fault, because the program was properly terminated, so no exploit possible. A backtrace is also printed to help.
It is possible to detect if a program is using such checks by using objdump:
[home ~/harden] objdump -d bad | grep call |grep _chk
84b: e8 c0 fe ff ff callq 710 <__stack_chk_fail@plt>
If there is one line or more, FORTIFY_SOURCE is active. However if the output is empty, FORTIFY_SOURCE either might not be enabled, or the program code is such that FORTIFY_SOURCE is not applicable (for example: secure code that has no static buffers and always checks buffer sizes, and thus FORTIFY_SOURCE cannot identify any potentially dangerous operations in the program).