0% found this document useful (0 votes)
32 views33 pages

07 Software Security 4

Uploaded by

Souhila
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views33 pages

07 Software Security 4

Uploaded by

Souhila
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 33

3761

Security Core Lecture


07 Software Security IV –
Mitigations and Code-Reuse Attacks
Prof. Dr. Thorsten Holz | 14.11.2023
3761

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

Recap: Stack-based Buffer Overflows


§ Function return will transfer control to shellcode
- ret pops saved rip from stack, which is now
attacker-controlled
- If attacker can approximate/guess buffer
address, they can specify target
Assembly Code of Buffer Overflow Example

push rbp
mov rbp, rsp

sub rsp, 0x200


mov esi, 0x4006ac
call 400480 <fopen@plt>

mov rcx, rax


mov edx, 0x1
mov esi, 0x400
lea rdi, [rbp-0x200]
call 400450 <fread@plt>

movsx eax, BYTE PTR [rbp-0x200]

leave
ret ; pop and jump to &shellcode 3
3761

Buffer Overflow Defenses


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

Limitations of Stack Canaries


§ Although widely used in practice, stack canaries have limitations
§ No protection of local variables
- Only saved rbp/rip are protected, local variables can still be overwritten
- Requires careful reordering of local variables
§ Little security guarantees if random canary leaks (e.g., memory leak)
- Attacker can overwrite canary with leaked value
- To mitigate this, compilers usually combine random with terminator
canaries
§ Only protects against stack-based buffer overflows
- Buffer overflows implicitly overwrite consecutive memory ranges
- Other attacks may overwrite the heap or just specific memory locations on
the stack (e.g., non-linear writes, heap overflows, etc.; see later)
11
3761

Limitations of Stack Canaries II


§ Less protection for auto-forking processes without re-randomization
§ Canary can be leaked via blind hacking
- Assume a process forks for every new client connection (e.g., web server)
- Attacker can operate on child process that shares canary with parent
- Crash of child is a side channel to indicate if the guessed canary was wrong
§ Idea: Byte-by-byte trial-and-error on canary
- Requires max 256 attempts per byte, i.e., 1024x for 4B and 2048x for 8B canary

12
3761

Return Address XORing


§ Similar idea to canaries: XOR return address with
random value Implementation of Return
Address XORing
- Attacker can overwrite saved rip, but decryption
will clobber its value myfun:
push rbp
- In function prologue: encrypt saved rip mov rbp, rsp

- In function epilogue: decrypt saved rip mov rcx, [xorsecret]


xor [rbp+8], rcx
xor rcx, rcx

§ Similar security guarantees, but ...

- Even secure if only saved rip is overwritten by mov rcx, [xorsecret]


attacker (in contrast, canary assumed xor [rbp+8], rcx
xor rcx, rcx
contiguous overwrite)
leave
- Also susceptible to blind hacking, as value is not ret
re-randomized
13
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

§ MMU enforces non-executable stack


- Basic principle: a page is either writable (W), or executable (X), never W+X
- NX bit in page table helps to enforce this write-xor-execute (W^X) policy
- In Windows, this scheme is called Data Execution Prevention (DEP)

§ It is no longer possible to execute data (➡ stack)


- Shellcode placed on the stack cannot be executed ...
- ... unless privileges are set accordingly (e.g., mprotect(), -z execstack)
14
3761

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

Principle of Code Injection Attacks


3761

Principle of Code Reuse Attacks

No new node in CFG


but new edge!

18
3761

Code Injection vs. Code Reuse


§ Code injection: Adding new node to CFG
- Adversary can execute arbitrary malicious code
§ Code reuse: Adding new path to CFG
- Adversary is limited to code nodes that are available in CFG
- Requires reverse-engineering and static analysis of the code base of a
program
§ Attacks are often hybrid and combine both techniques
- First reuse existing code to make memory executable
- Then execute that memory

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

Return-to-Libc for x86

Buffer overflow Function returns

23
3761

Intuitive Overview

§ Insights
- Generate arbitrary text
- Can even use characters
in middle of words
- Can we transfer this to
exploits?

Re t u r n o r ien ted Pro g ra mm ing

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

Corrupt Control Sequence 2


Structures
pop REG1
pop REG2
Program Stack
ret
Return Address 3 Sequence 3

Value 2 asm_ins
ret
Value 1
Return Address 2 REG1: Value 1
SP Return Address 1 REG2: Value 2

27
3761

Summary of Basic Idea


§ Perform arbitrary computation with generalized return-into-libc techniques:
- Use small instruction sequences (e.g., of libc or other libraries) instead of
using whole functions
- Instruction sequences range from 2-5 instructions
- All sequences end with a return instruction
- Instruction sequences chained together to a gadget; perform a particular
task (e.g., load, add, or branch)
- Desired actions performed by combining gadgets

28
3761

Gadget Space on Different Architectures

Architectures with no memory Architectures with memory


alignment (e.g., Intel x86) alignment (e.g., ARM)

29
3761

Eliminating Unaligned Gadgets


§ x64 instructions have variable length (1 – 15 bytes)
- By default, there is no enforcement where an instruction starts
- Unaligned gadgets might be hidden in other instructions
3761

Eliminating Unaligned Gadgets II


§ Unaligned gadgets can be eliminated by rewriting
- Basic idea: Destroy values containing control flow instructions (e.g., ret, jmp)
- Rewriting must not change semantics of code
§ Multiple rewritings required
- Align two consecutive instructions with nop sled between them
- Reallocate registers if one register turns ModR/M byte into gadget
- Transform two-byte opcodes that contain 0xc3 (ret)
- Transform immediates (see example below)

31
3761

ROP Gadget Search


§ Searching gadgets with ROPgadget
cd /usr/local/src/ROPgadget
python ROPgadget.py --binary /lib/x86_64-linux-gnu/libc-2.31.so --all | less –S

§ Can also be done from pwndbg, etc


- ex. pwndgb: ropper -- --search "pop rdi"
§ Searching gadgets with pwntools
Gadget search in pwntools

# load file where we want to find gadgets in


rop = ROP(ELF('libc.so’))

# find gadgets using any search term, can also be 'pop rdi' only
rop.find_gadget(['pop rdi', 'ret’])

# find gadgets that store specified values in the specified registers


rop.setRegisters({'rdi': 0x1234, 'rsi': 0x2345})

32
3761

Sources / References

§ Overflow function return address


- Phrack 49:“Smashing the Stack for Fun and Profit” by Aleph One
- Phrack 58: “The advanced return-into-lib(c) exploits” by Nergel
§ Overflow function frame (base) pointer
- Phrack 55: “The Frame Pointer Overflow” by klog
§ Overflow (dynamically allocated) memory region on the heap
- Phrack 57: “Vudo malloc tricks” by MaXX
- Phrack 57: “Once upon a free()...” by anonymous
§ Overflow function pointers
- Many different possibilities, for example stack, heap, BSS (e.g., PLT)
§ Video tutorials on YouTube by LiveOverflow
34

You might also like