Security countermeasure strikes at several level:
Focusing on C/C++:
Serge « sans paille » Guelton
Compiler Engineer / Wood Craft Lover / Red Hat employee
LPC'20 — 26th of August 2020
-O2 -g -pipe -Wall -Werror=format-security \ -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS \ -fexceptions -fstack-protector-strong \ -grecord-gcc-switches \ -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 \ -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 \ -mtune=generic -fasynchronous-unwind-tables \ -fstack-clash-protection -fcf-protection \ -Wl,-z,relro -Wl,--as-needed -Wl,-z,now \ -specs=/usr/lib/rpm/redhat/redhat-hardened-ld
-g -O2 \ -fdebug-prefix-map=/home/sylvestre/dev/debian/pkg-llvm\ /llvm-toolchain/branches=. -fstack-protector-strong \ -Wformat -Werror=format-security -Wl,-z,relro
Attack: Exploit standard C/C++ functions misuse
Countermeasure: Provide fortified version of these functions
Flag: -D_FORTIFY_SOURCE (gcc, clang for builtin supports), -D_GLIBCXX_ASSERTIONS
Overhead: low (fortify) to high (asserts)
Artifact: nm a.out | grep __strcpy_chk
Attack: Exploit user-controlled formating arguments
Countermeasure: Warn about dubious patterns
Flag: -Werror=format-security (gcc, clang)
Overhead: nop (compile time)
Attack: Exploit buffer overflow
Countermeasure: Range analysis
Flag: -Werror=array-bounds (gcc, clang)
Overhead: nop (compile time)
Attack: Use uninitialized variable to leak previous state
Coutermeasure: Always initialize stack variable
Flag: -ftrivial-auto-var-init=pattern (clang)
Overhead: yes (?)
Attack: Overwrite the GOT/PLT to overwrite executable sections
Countermeasure: Load everything then mark GOT/PLT read-only
Flag: -Wl,-z,relro, -Wl,-z,now (ld.bfd, lld)
Overhead: increased startup time
Artifact: readelf -a now | grep BIND_NOW
Attack: Overwrite an executable stack with malicious code
Countermeasure: Mark the stack as non-executable
Flag: -Wl,-z,noexecstack (ld.bfd, lld)
Overhead: nop (?)
Artifact: readelf -e a.out | { ! grep -E 'GNU_STACK.*RWE' ; }
Attack: Use hardcoded adress in shellcodes/others
Countermeasure: Randomize process adresses (ASLR)
Flag: -pie -fPIE or -fPIC (gcc/ld.bfd, clang/lld) + /proc/sys/kernel/randomize_va_space
Overhead: relative jump computation
Artefact: readelf -e a.out | grep 'DYN (Shared object file)'
Attack: Make the stack and the heap grow so that they overlap
Countermeasure: Probe each page to trigger the kernel page guard
Flag: -fstack-clash-protector (gcc, clang)
Overhead: only for functions with large / dynamic stack alloc
Artefact: objdump -S a.out | grep 'subq 4096, %rsp'
Attack: Modify the stack thanks to an overflow
Countermeasure: Stack Canary, Split Stack
Flag: -fstack-protector-strong (gcc, clang), -fsanitize=safe-stack (clang)
Overhead: one check per function, user-controlled granularity
Artefact: nm a.out | grep __stack_chk_fail
All these slides were pretty classic, right?
Attack: Trick branch prediction into filling the cache with secret data
Countermeasure: create a data dependency between data access and predicate state
Flag: -mspeculative-load-hardening (clang)
Overhead: non-neglectible
Attack: Trick branch prediction into executing a controlled function pointer
Countermeasure: Use return prediction instead of branch prediction
Flag: -mretpoline (clang) -mindirect-branch, -mfunction-return (gcc)
Overhead: non-neglectible
Attack: Execute arbitrary code through a chain of gadget
Countermeasure: Check Control Flow Integrity / Intel CET, ARM BTI
Flag: -fsanitize=cfi (clang) -fcf-protection (clang, gcc)
Want to double-check the flags used in the build process?
Artefact: readelf a.out -p .GCC.command.line | grep record-gcc-switches
For each compiler flag, test for harderning artefacts, à la hardening-check.
- instrumentation-based verification of distance invariant?
- Static verification?