07 Software Security 4
07 Software Security 4
Further Reading
§ Cowan et al.: „StackGuard: Automatic Adaptive Detection and Prevention of
Buffer-Overflow Attacks”, USENIX Security’98
§ Krahmer: „x86-64 buffer overflow exploits and the borrowed code chunks
exploitation technique“, 2005
§ Shacham et al.: „The Geometry of Innocent Flesh on the Bone: Return-into-
libc without Function Calls (on the x86)”, ACM CCS’07
§ Van der Veen et al.: “The Dynamics of Innocent Flesh on the Bone: Code
Reuse Ten Years Later”, ACM CCS’17
§ Shoshitaishvili et al.: „SoK: (State of) The Art of War: Offensive Techniques in
Binary Analysis“, IEEE S&P’16
§ Dang et al.: „The Performance Cost of Shadow Stacks and Stack Canaries“,
ASIACCS’15
1
3761
This Lecture
§ Last lectures
- User vs. kernel mode
- (Stack-based) Buffer overflows
• Data corruption, code execution, shellcode, NOP sleds, zero bytes, …
§ This lecture
- Buffer overflow defenses
• Stack canaries, return address XORing, non-executable stack, and
shadow stack
- Code-reuse attacks and return-oriented programming (ROP)
2
3761
push rbp
mov rbp, rsp
leave
ret ; pop and jump to &shellcode 3
3761
Canaries
Canaries were used in coal mines as an early-warning signal for toxic gases
(e.g., carbon monoxide) - if the canary passes out/dies, alarm!
6
3761
Stack Canaries
§ Idea: Place canary (i.e., a special value) on stack before saved rbp/rip
§ (Linear) Overflow also has to overwrite the canary in order to overwrite saved
rip/rbp
§ Before executing ret, program compares canary
§ Abort if canary has been manipulated
7
3761
Stack Canaries II
§ Canary implementation details
- gcc/LLVM store canary in thread-local storage (pointed to by fs register)
Stack Canaries as Implemented by gcc
push rbp
mov rbp,rsp
mov rax,fs:0x28
mov [rbp-0x8],rax
xor eax,eax
...
mov rdx,[rbp-0x8]
xor rdx,fs:0x28
je +5
call <__stack_chk_fail@plt>
leave
ret
8
3761
Random Canaries
§ Random canaries use a random value pre-generated at program start
- Attacker has to guess (or read/leak) canary to exploit successfully
- One-time-randomization, i.e., canary is shared among functions/threads
- Canary is initialized by ld-load (on Linux), and stored in the TLS (thread-
local storage). The fs register points to the general datastructure, 0x28 is
the offset of the canary within the datastructure
§ Why not per-function canaries?
- Slows down performance
• Need to generate random canary per function
• Can no longer use single memory location
- Do not give much stronger guarantees: An attacker who can leak a canary
can likely also leak several canaries
9
3761
Terminator Canaries
§ Terminator canaries consist of bytes that terminate C strings
- Attacker who aims to overwrite canary has to insert these bytes
- Mitigates rip overwrites for several string functions
• gets() stops at newline character
• scanf("%s") stops at white-space character
• strcpy() stops at NULL byte
- Example: 32-bit terminator canary could be 0x00 || '\n' || '\r' || 0xFF
§ Limited use case
- No randomization, hence only stops string functions (and not, e.g., read())
- gcc falls back to terminator canaries if it cannot find source for randomness
§ In practice, randomized and terminator canaries are combined
- gcc’s canaries consist of 6 random and 2 terminator bytes
- Attacker has to overwrite terminator bytes before reaching the random bytes
10
3761
12
3761
Non-Executable Stack
§ Non-executable stack prevents classical shellcode attacks
- Shellcode could execute because we assumed stack to be executable
- However, stack should just contain data which is non-executable
Shadow Stack
§ Idea of shadow stack: store rip twice
- Function calls store rip on main stack, but attackers might overwrite stack
- Shadow stack: Instrument function calls to store rip in a second, “secret” location
- Before returning, check if rip on shadow stack matches rip on normal stack
§ Two implementation alternatives
- Full shadow stack copies memory layout of normal stack: fast, but large memory footprint
- Dense shadow stack is consecutive array of return addresses: less memory, but slower
15
3761
Code-Reuse Attacks
3761
18
3761
19
3761
Code-Reuse Attacks
§ Code execution by reusing existing code
- Assume that the stack is non-executable (DEP/NX-bit)
- We can overrun the buffer, but cannot add shellcode
- Idea: re-use code that by definition is executable
C code
char *mystr(char *fn) {
char mystr[32];
reg FILE *f = fopen(fn, "rb");
fread(mystr, 2048, 1, f);
return NULL;
}
20
3761
Return-to-Libc
§ Code execution by reusing existing functions
- Attack can re-use any of the linked system functions
- Instead of placing a shellcode, attacker diverts control flow to such functions
- Function code is executable by design
§ Attack known as return-to-libc, as libc library is a prominent jump target
21
3761
Return-to-Libc II
§ Example: return to start of system(char *cmd) to execute cmd
- In x86, function parameter could have just been placed on the stack
- In x64, attacker has to store the first parameter in rdi
22
3761
23
3761
Intuitive Overview
§ Insights
- Generate arbitrary text
- Can even use characters
in middle of words
- Can we transfer this to
exploits?
24
3761
Return-Oriented Programming
§ Code execution by reusing existing functions
- Reuse code gadgets, i.e., not entire functions, but just part of functions
- In principle, we can return to the middle of a function (and even instruction!)
- Code gadget ends, e.g., in ret or jmp <reg>
§ In our example, we need a gadget that stores stack element in rdi
25
3761
Return-Oriented Programming II
§ We can chain several function calls on the stack
- Example: If we want to nicely clean after shell terminates, call exit(0)
26
3761
ROP Example
Program Code
Sequence 1
asm_ins
ret
Value 2 asm_ins
ret
Value 1
Return Address 2 REG1: Value 1
SP Return Address 1 REG2: Value 2
27
3761
28
3761
29
3761
31
3761
# find gadgets using any search term, can also be 'pop rdi' only
rop.find_gadget(['pop rdi', 'ret’])
32
3761
Sources / References