Professional Documents
Culture Documents
EU 22 Duta Unwinding The Stack For Fun and Profit
EU 22 Duta Unwinding The Stack For Fun and Profit
void callme() {
// do things
}
Stack
int main() {
callme();
// more things
}
0x41414141
void callme() {
// overflow
}
Stack
int main() {
callme();
// more things
}
main + 5
void callme() {
// overflow
}
Stack
int main() {
callme();
// more things
}
main + 5
Shadow Stack
return C
Backward edge Forward edge
Control-flow Control-flow
Hijack Hijack
handler_1
SEH (Windows)
return
Single Linked List
handler_2 of pointers to handlers
handler_3
handler_1
SEH (Windows)
return
Single Linked List
handler_2 of pointers to handlers
evil
handler_1
SafeSEH
return
Allowed List of Handlers
handler_2
● handler_1
● handler_2
return ● handler_3
evil not in list!
evil
Stack Buffer
Overflows
are a solved Any questions?
problem!
Thank you for listening!
Are there any exceptions
to these mitigations?
Yes!
CHOP
Control Flow Hijacking
by confusing the unwinder
In a nutshell
unwinder
😕 compilers
CH
OP
defenses
handler evil
NDSS 2023
RG O
M B A 02 0 23
E J a n 1
Roadmap
Exploitation
Techniques
General Stats
Idea
Real-World
Exploits
Unwinding
void foo() { Stack
try {
bar();
}
catch (...) {
// handle errors
} foo + 5
} unwinding
void bar() {
throw new Exception();
}
Exception Tables
Landing
Call Site Exception Metadata
Pad
0x1000..0x1004 * 0x1042
0x2030
0x3042
throw
bad_alloc;
0x1002 Return address
throw
bad_alloc;
0x1002
0x2030
0x3042
throw
bad_alloc;
0x1002 Call Site
Exception Landing
Metadata Pad
0x1000..0x1004 * 0x1042
0x3042
throw
bad_alloc;
0x1002 Call Site
Exception Landing
Metadata Pad
0x1000..0x1004 * 0x1042
No Handler in Table
0x3042
throw
bad_alloc;
0x1002 Call Site
Exception Landing
Metadata Pad
0x1000..0x1004 * 0x1042
Not a runtime_error!
0x3042
throw
bad_alloc;
0x1002 Call Site
Exception Landing
Metadata Pad
0x1000..0x1004 * 0x1042
We found a handler!
throw
bad_alloc;
Roadmap
Exploitation
Techniques
General Stats
Idea
Real-World
Exploits
Unwinding
void foo() { Stack
try {
bar();
}
catch (...) {
// handle errors
} foo + 5
}
void bar() {
void bar() {
// overflow!
throw new Exception();
}
void foo() {
try {
vuln();
Catch Handler
} Confusion
catch (...) { lose(); }
}
void vuln() {
// overflow
throw new Exception();
}
General Stats
Idea
Real-World
Exploits
Unwinding
Stack Buffer
Overflow
Viable Handler
Are there interesting
handlers we can reach?
throw
catch
Golden Gadget in libstdc++
void __cxa_call_unexpected (void *exc_obj_in) {
xh_terminate_handler = xh->terminateHandler;
try { /* ... */ }
catch (...) {
Restored
__terminate(xh_terminate_handler); from stack
}
}
if (!RequiresStackProtector())
return false;
// ...
return InsertStackProtectors();
}
llvm::StackProtector
/// Check whether or not this function needs a stack protector based
/// upon the stack protector level.
///
/// We use two heuristics: a standard (ssp) and strong (sspstrong).
L ; R
/// The standard heuristic which will add a guard variable to functions that
D
/// call alloca with a either a variable size or a size >= SSPBufferSize,
/// functions with character buffers larger than SSPBufferSize, and functions
T
/// with aggregates containing character buffers larger than SSPBufferSize.
The
/// strong heuristic will add a guard variables to functions that call alloca
/// regardless of size, functions with any buffer regardless of type and size,
/// functions with aggregates that contain any buffer regardless of type and
/// size, and functions that contain stack-based variables that have had their
/// address taken.
bool StackProtector::RequiresStackProtector() {
llvm::StackProtector
➔ Functions that call alloca()
return abort
throw Handler
return abort
➔ No alloca
➔ No buffer
➔ No addr taken
➔ No cookie!
return abort
➔ No alloca
➔ No buffer
➔ No addr taken
over ➔ No cookie!
flow
throw Handler Handler
baz();
// unreachable
}
1. foo() {
try { bar(); }
catch(...) {...}
}
3. baz() - throws class Thing {
2. bar() { Thing() {}
Thing it();
~Thing() {
baz();
// unreachable // cleanup
} } Unwind_Resume
1. foo() { }
try { bar(); }
catch(...) {...}
}
Cleanup Handlers
➔ Often call
free()
➔ Can be chained
(similar to ROP)
libstdc++
💀
kill -SIGUSR1 $pid
⚙⏸ ???
Signal
Handler
return
sigreturn
frame
48 c7 c0 0f
00 00 00 0f
return
05
return
fake sigreturn
frame
48 c7 c0 0f
00 00 00 0f
return
05
return return
fake sigreturn
frame
Handler
RSP
RIP
48 c7 c0 0f
00 00 00 0f
return
05
Recap: Techniques
General Stats
Idea
Real-World
Exploits
Unwinding
Stack Buffer
Overflow
Preconditions
throw
Let’s recap
Viable Handler
Select top 1000 popular
packages (~3.3k binaries)
~10% of Binaries
Debian repo use exception handling
Half contain
Debian Binary at least 40%
DB throwing functions
PopCon Analysis
Interesting gadgets Cleanup Catch
➔ Arbitrary Free 79 % 58 %
➔ Control-flow Hijack 64 % 51 %
➔ Write-What-Where 44 % 46 %
➔ Write-Where 66 % 49 %
➔ Write-What 2 % 17 %
% of binaries containing
at least one gadget
Roadmap
Exploitation
Techniques
General Stats
Idea
Real-World
Exploits
Unwinding
● PowerDNS
Snap Back CVE-2009-4009
to Reality ● SmartCardServices
CVE-2018-4300
Exploiting
Known Bugs ● LibRaw
CVE-2018-5809
● PowerDNS
Snap Back CVE-2009-4009
to Reality ● SmartCardServices
CVE-2018-4300
Exploiting
Known Bugs ● LibRaw
CVE-2018-5809
parse_exif (int base) {
// ... CVE-2018-5809
while (entries--) {
tiff_get (base, &tag, &type, &len, &save);
// ...
switch (tag) {
// ...
case 37500: // tag 0x927c
if ((!strncmp(make, "RaspberryPi",11)) &&
(!strncmp(model, "RP_imx219",9)))) // and others
char mn_text[512];
// ...
fgets(mn_text, len, ifp);
// no throw :(
}
parse_exif (int base) {
// ... CVE-2018-5809
while (entries--) {
tiff_get (base, &tag, &type, &len, &save);
// ...
switch (tag) {
// ...
case 37500: // tag 0x927c
if ((!strncmp(make, "RaspberryPi",11)) &&
(!strncmp(model, "RP_imx219",9)))) // and others
char mn_text[512];
// ...
fgets(mn_text, len, ifp);
// no throw :(
}
else
parse_makernote (base, 0);
RaspberryPi
return
return
mn_text parse_exif
PwnpberryPi Now we can trigger throw!
return
one_gadget
/bin/sh
Pivot2ROP
mn_text parse_exif
INT64 LibRaw::x3f_thumb_size()
{
try
{
// address to here
// in the return address
}
catch (...)
{
return -1;
}
}
Demo Time
parse_exif (int base) {
// ... CVE-2018-5809
while (entries--) {
tiff_get (base, &tag, &type, &len, &save);
// ...
switch (tag) {
// ...
case 37500: // tag 0x927c
if ((!strncmp(make, "RaspberryPi",11)) &&
(!strncmp(model, "RP_imx219",9)))) // and others
char mn_text[512];
// ...
fgets(mn_text, len, ifp);
// no throw :(
}
else
parse_makernote (base, 0);
If we corrupt the stack,
we can modify data
to reach throw
via unexpected paths!
Recap
Exploitation
Techniques
General Stats
Idea
Real-World
Recap Exploits
Unwinding
● The entire research team
● Tasteless
● Jiska Classen