An Introduction to Buffer Overflow Exploits Bart Coppens.
-
Upload
harold-atkinson -
Category
Documents
-
view
236 -
download
2
Transcript of An Introduction to Buffer Overflow Exploits Bart Coppens.
General Note
This slide deck is based on a set of presentations I gave, so take note that:• I tend to use lots of figures and give lots of additional detail by
explaining those figures, rather than adding text. Slides without my (over)enthousiastic explanations lose a lot of that
• I also gave some very quick demo’s, you’ll miss those too• There were some hands-on excercises (including a real-life one
based on a proftpd vulnerability from 2010), but I haven’t cleaned those up yet
• I had some ‘zen’ slides consisting of CC-licensed pictures of cute puppies, kittens, etc. to make the audience feel less brain-dead, but they made this merged presentation’s file size a bit too large
Exploits 1:Basic Buffer Overflows & Shellcode
Bart Coppens
We’ll be abusing vulnerabilities in code
A kind of vulnerability The attack code we’llbe executing
Structure
• Basic exploits– Intro to buffer-overflows, exploits & shellcode– No protections, just like the good old days of the
mid-‘90s (executable stacks, no ASLR, …)• Advanced exploits– Protections against buffer-overflow-based exploits
(non-executable stacks, ASLR, …)– How to circumvent these (ROP, …)
Part 1: Basic Exploitsx86-64 assembly crash course
A look at buffers are implemented in assembly
Buffer overflows
How to execute the attack code
Normal program behavior
xor %rax,%raxretq
callq function
int function() { return 0;}
…int i = function();… movq %rax, …
Normal program behavior
rsp
xor %rax,%raxretq
callq function
movq %rax, …
Higher addresses
Stack frame of main
Normal program behavior
Stack frame of main
rsp Return address to main
xor %rax,%raxretq
callq function
movq %rax, …
Higher addresses
Normal program behavior
xor %rax,%raxretq
callq function
movq %rax, …
rsp
Higher addresses
Stack frame of main
Local variables and the stack
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
int silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
function(%rdi, %rsi, %rdx)
Local variables and the stack
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
int silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
function(%rdi, %rsi, %rdx)
Local variables and the stack
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
int silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
function(%rdi, %rsi, %rdx)
Local variables and the stack
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
int silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
function(%rdi, %rsi, %rdx)
0x64 bytes
Local variables and the stack
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
int silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
rsp Return address to main
rsp
buffer[0]
buffer[99]
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
0x64 bytes
rsp
rdi sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
0x64 bytes
Return address etc
rdi
rsp
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
0x64 bytes
Return address etc
rdi
rsp
… sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
H e l l
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
0x64 bytesrdi
rsp … sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
H e l l
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
0x64 bytesrdi
rsp … sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
H e l l
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
rdi
rsp
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
rdi
rsp
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(50, "Hello\n");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
0x64 bytesrdi
rsp …
buffer[100]
buffer[99]
buffer[107]
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
H e l l
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "Hello…ABCDEFGH");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
Return address to main
0x64 bytesrdi
rsp …
buffer[100]
buffer[99]
buffer[107]
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
H e l l
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "Hello…ABCDEFGH");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
B C D E F G
0x64 bytesrdi
rsp …
buffer[100]
buffer[99]
buffer[107]A H
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
H e l l
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "Hello…ABCDEFGH");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
rsp B C D E F G
rdi
…
A H
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "Hello…ABCDEFGH");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
rsp B C D E F G
rdi
…
A H
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "Hello…ABCDEFGH");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
B C D E F G
rdi
rsp
A H
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…ABCDEFGH");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
B C D E F G
rdi
rsp
A H
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…ABCDEFGH");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
B C D E F G
rdi
A H
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
rsp
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
0 0 0 0 1 2
rdi
0 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
rsp
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
rsp 0 0 0 0 1 2
rdi
0 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
Local variables and the stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack frame of main…
function(%rdi, %rsi, %rdx)
rsp 0 0 0 0 1 2
rdi
0 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
Demo
Sorry, here was a live demo that starts from showing how certain inputs crash a program and analysing that in gdb, to writing & executing very simple shell code. Maybe one day I’ll make screenshots + captions
Overview
• Recap• Non-executable stack & ROP• Function pointers• Address Space Layout Randomization• Stack reading• Hands-on!
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
return address
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
buffer
0x123
rsp
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
rsp
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
rsp
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
rsp
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x123
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
rsp
0x12F
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x12F
rsp
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
0x12F
rsp
(garbage instructions)
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
nopnopnopmovq $0, %rax…
rsp NOP slide/sled
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
nopnopnopmovq $0, %rax…
NOP slide/sled
rsp
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
nopnopnopmovq $0, %rax…
NOP slide/sled
rsp
Executable stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
nopnopnopmovq $0, %rax…
NOP slide/sled
rsp
Non-Executable Stackint silly_function(int len, char* src) { char buffer[100]; memcpy(buffer, src, len); return 0;}
int main() { return silly_function(108, "\x48\xc7…230100…");}
adresses
Stack of ‘main’…
function(%rdi, %rsi, %rdx)
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
rsp
Stack memory mappedas non-executable!
Non-Executable Stack
adresses
Stack of ‘main’…
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
movq $0, %rax…syscall
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
4KR,W
rsp
Non-Executable Stack
adresses
Stack of ‘main’…
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
4KR,W
Return-to-libc
adresses
Stack of ‘main’…
0 0 0 0 1 20 3
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
4KR,W
Return-to-libc
adresses
Stack of ‘main’…
address of exit
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
4KR,W
Return-to-libc
adresses
Stack of ‘main’…
address of system
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>xor %rax,%raxadd $0x64, %rspretq
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
4KR,W
Return-to-libc
adresses
Stack of ‘main’…
address of systemesp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
32 bit
4KR,W
…xor %eax,%eaxadd $0x64, %espret
Return-to-libc
adresses
Stack of ‘main’…
address of system
esp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
32 bit
garbage (‘return addr.’)
argument 1
argument 2
argument 3
4KR,W
…xor %eax,%eaxadd $0x64, %espret
overflown buffer
Return-to-libc
adresses
Stack of ‘main’…
address of systemesp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
32 bit
garbage (‘return addr.’)
argument 1
argument 2
argument 3
4KR,W
…xor %eax,%eaxadd $0x64, %espret
overflown buffer
Return-to-libc
adresses
Stack of ‘main’…
address of system
esp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
32 bit
garbage (‘return addr.’)
argument 1
argument 2
argument 3
4KR,W
…xor %eax,%eaxadd $0x64, %espret
overflown buffer
Return-to-libc
adresses
Stack of ‘main’…
address of system
esp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
32 bit
garbage (‘return addr.’)
argument 1
argument 2
argument 3
4KR,W
…xor %eax,%eaxadd $0x64, %espret
overflown buffer
Return-to-libc
adresses
Stack of ‘main’…
address of system
esp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
32 bit
garbage (‘return addr.’)
ptr. to “/bin/sh”
argument 2
argument 3
4KR,W
…xor %eax,%eaxadd $0x64, %espret
overflown buffer
Return-to-libc
adresses
Stack of ‘main’…
address of system
esp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
32 bit
garbage (‘return addr.’)
ptr. to “/bin/sh”
argument 2
argument 3
4KR,W
…xor %eax,%eaxadd $0x64, %espret
overflown buffer
can be used to executea second function!
Return-to-libc
adresses
Stack of ‘main’…
address of system
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
garbage (‘return addr.’)
ptr. to “/bin/sh”
argument 2
argument 3
4KR,W
…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
Return-to-libc
adresses
Stack of ‘main’…
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
4KR,W
xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
Return-Oriented Programming
adresses
Stack of ‘main’…
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget
Return-Oriented Programming
adresses
Stack of ‘main’…
&(pop-rdi; ret)rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
ptr. to “/bin/sh”
address of system
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget
Return-Oriented Programming
adresses
Stack of ‘main’…
&(pop-rdi; ret)
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
ptr. to “/bin/sh”
address of system
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget
Return-Oriented Programming
adresses
Stack of ‘main’…
&(pop-rdi; ret)
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
ptr. to “/bin/sh”
address of system
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget“/bin/sh”
Return-Oriented Programming
adresses
Stack of ‘main’…
&(pop-rdi; ret)
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
ptr. to “/bin/sh”
address of system
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget“/bin/sh”
Return-Oriented Programming
adresses
Stack of ‘main’…
&(pop-rdi; ret)
rsp
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
ptr. to “/bin/sh”
address of system
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget“/bin/sh”
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
…pop %rdiret…xor %rax,%raxadd $0x64, %rspret
overflown bufferArguments in registers!
fun(%rdi,%rsi,%rdx)
gadget
chai
ning
gad
gets
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
overflown buffer
chai
ning
gad
gets
0x81 0xfa 0x5c 0xc3 0x00 0x00
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
overflown buffer
chai
ning
gad
gets
0x81 0xfa 0x5c 0xc3 0x00 0x00
cmp $0xc35c, %edx
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
overflown buffer
chai
ning
gad
gets
0x81 0xfa 0x5c 0xc3 0x00 0x00
cmp $0xc35c, %edx
pop %rsp
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
overflown buffer
chai
ning
gad
gets
0x81 0xfa 0x5c 0xc3 0x00 0x00
cmp $0xc35c, %edx
ret
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
overflown buffer
chai
ning
gad
gets
0x81 0xfa 0x5c 0xc3 0x00 0x00
cmp $0xc35c, %edx
pop %rsp; ret
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
overflown buffer
chai
ning
gad
gets
• Pop-gadgets• Write-anywhere gadgets (mov %rdi, (%rsi);
ret)• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)• Sometimes it can be a simple stager:
• Write shellcode in RW memory (read)
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,W
overflown buffer
chai
ning
gad
gets
• Pop-gadgets• Write-anywhere gadgets (mov %rdi, (%rsi);
ret)• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)• Sometimes it can be a simple stager:
• Write shellcode in RW memory (read)• Make page executable (mprotect)
(not possible with PaX)
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,X
overflown buffer
chai
ning
gad
gets
• Pop-gadgets• Write-anywhere gadgets (mov %rdi, (%rsi);
ret)• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)• Sometimes it can be a simple stager:
• Write shellcode in RW memory (read)• Make page executable (mprotect)
(not possible with PaX)• ‘Return’ to the (executable) shellcode
Return-Oriented Programming
adresses
…
&(pop-rdi; ret)
proftpd libc stack
0 0x7ff…
4KR,X
4KR,X
4KR,X
4KR,X
4KR,W
4KR,W
4KR,X
exit, read, write,system, mprotect, …
64 bit
0x20&(pop rsi;pop
rdx;ret)
0x23
0xFF73
4KR,X
overflown buffer
chai
ning
gad
gets
• Pop-gadgets• Write-anywhere gadgets (mov %rdi, (%rsi);
ret)• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)• Sometimes it can be a simple stager:
• Write shellcode in RW memory (read)• Make page executable (mprotect)
(not possible with PaX)• ‘Return’ to the (executable) shellcode
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…
pos
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
system
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
systemisCute
isCuddly
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
systemisCute
isCuddlyfake vtable
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
pop …retisCute
isCuddlyfake vtable
local varsret addr. main
rsp
…
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
pop …retisCute
isCuddlyfake vtable
ret. addr calllocal vars
ret addr. main
rsp
…
Not under our control!No ROP possible?
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
pop …retisCute
isCuddlyfake vtable
ret. addr calllocal vars
ret addr. main
rsp
…
normalROP
chain…
Some bufferrdi
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
mov %rdi, %rspretisCute
isCuddlyfake vtable
ret. addr calllocal vars
ret addr. main
rsp
…
normalROP
chain…
Some bufferrdi
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
mov %rdi, %rspretisCute
isCuddlyfake vtable
ret. addr calllocal vars
ret addr. main
rsp
…
normalROP
chain…
Some bufferrdi
rsp
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
mov %rdi, %rspretisCute
isCuddlyfake vtable
ret. addr calllocal vars
ret addr. main…
normalROP
chain…
Some bufferrdi
rsp
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
mov %rdi, %rspretisCute
isCuddlyfake vtable
ret. addr calllocal vars
ret addr. main…
normalROP
chain…
Some bufferrdi
rsp
Overwriting function pointersstruct Animal {
virtual bool isCute();virtual bool isCuddly();Position pos;
};Animal* a = …;a->isCuddly();…movq (…), %raxmovq 0x8(%rax), %raxcallq *%rax
vtable
pos
isCuteisCuddly
Dog::isCute
Labrador::isCuddly
xchg %rdi, %rspretisCute
isCuddlyfake vtable
ret. addr calllocal vars
ret addr. main…
normalROP
chain…
Some bufferrdi
rsp
Function pointers to libraries
libc stack
exit
proftpd
code
data
exit@PLT:jmp GOT[exitid]
call exit@PLT
GO
TPL
T
symbol resolver stub
Function pointers to libraries
libc stack
exit
proftpd
code
data
exit@PLT:jmp GOT[exitid]
call exit@PLT
GO
TPL
T
symbol resolver stub
Function pointers to libraries
libc stack
exit
proftpd
code
data
exit@PLT:jmp GOT[exitid]
call exit@PLT
GO
TPL
T
symbol resolver stub
all function pointers in the Global Offset Tablecan be overwritten, next time application callsexit, attack code is executed
Address Space Layout Randomization
proftpd libc stack
adresses
Stack of ‘main’…
address of exitrsp
Randomize on each program start:• Load address of libraries• Stack base• Heap base
Address Space Layout Randomization
proftpd libc stack
32 bit
page offset…
>=4 bits 12 bits<=16 bitsBrute-forcable
64 bit
page offsetcanonical address
16 bits 12 bits>=28 bitsBrute forcing harder
…
Address Space Layout Randomization
proftpd libc stack
Stack of ‘main’…
gadgets in proftpdrsp
write@PLTAt fixed address!
Address Space Layout Randomization
proftpd libc stack
Stack of ‘main’…
gadgets in proftpdrsp
Only possible with Position Independent Executable (PIE)
Stack canary/cookie
return address
0x64 bytes
buffer …
buffer[99]
buffer[107]
H e l l
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>add $0x64, %rspretq
Stack Smash Protector / Cookies
return address
0x64 bytes
buffer …H e l l
stack cookie/canary
sub $0x64, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>add $0x64, %rspretq
Stack Smash Protector / Cookies
return address
0x64 bytes
buffer …H e l l
stack cookie/canary
sub $0x6C, %rspmovq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>add $0x6C, %rspretq
Stack Smash Protector / Cookies
return address
0x64 bytes
buffer …H e l l
sub $0x6C, %rspmov %fs:0x28,%raxmov %rax,0x64(%rsp)movq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>add $0x6C, %rspretqstack cookie/canary
Per-process value initialized by loaderSegment-relative addressing hard to leak value
Stack Smash Protector / Cookies
return address
0x64 bytes
buffer …H e l l
sub $0x6C, %rspmov %fs:0x28,%raxmov %rax,0x64(%rsp)movq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>mov 0x64(%rsp),%raxxor %fs:0x28,%raxjne … stack_chk_failadd $0x6C, %rspretq
stack cookie/canary
Per-process value initialized by loaderSegment-relative addressing hard to leak value
Stack Smash Protector / Cookies
return address
0x64 bytes
buffer …H e l l
sub $0x6C, %rspmov %fs:0x28,%raxmov %rax,0x64(%rsp)movq %rdi, %rdxmovq %rsp, %rdicallq <memcpy>mov 0x64(%rsp),%raxxor %fs:0x28,%raxjne … stack_chk_failadd $0x6C, %rspretq
stack cookie/canary
Per-process value initialized by loaderSegment-relative addressing hard to leak value
saved rbx, …
local variables
(argument copies)
Information leakage
Unknown to attacker:• Stack/code addresses• CookieLeak information back to attacker first:• Leak pointer data, use tweaked exploit based on data
– Read outside array bounds– Format string vulnerabilities (not that common anymore?)– Serialization– Timing side channels
• In scripting languages: create exploit directly in the script• Bruteforcing (if possible)• Generalized stack reading…
Generalized Stack Reading
proftpd libc stack
Client
Waiting for clients…
connect
…
return address
rsp cookie
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
Waiting for clients…accept() connectionfork()connect
proftpd libc stack
…
return address
rsp cookieClone of the parent process:Same addresses, same cookie!
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
proftpd libc stack
…
return address
buffercookie
O v e r f l
Waiting for clients…
buffer = “Overflow”
send(buffer)Connection+Server ok?
√
√
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
proftpd libc stack
…
return address
buffercookie
O v e r f l0
Waiting for clients…
buffer = “Overflow”cookie_guess = [0x00]send(buffer + cookie_guess)Connection+Server ok?
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
Waiting for clients…
buffer = “Overflow”cookie_guess = [0x00]send(buffer + cookie_guess)Connection+Server ok?
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
connect
proftpd libc stack
…
return address
buffercookie
O v e r f l1
Waiting for clients…
buffer = “Overflow”cookie_guess = [0x01]send(buffer + cookie_guess)Connection+Server ok?
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
proftpd libc stack
…
return address
buffercookie
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie_guess = [0x02]send(buffer + cookie_guess)Connection+Server ok!
√
√
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
proftpd libc stack
…
return address
buffercookie
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie_guess = [0x02, 0x00]send(buffer + cookie_guess)Connection+Server ok?
0
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
connect
proftpd libc stack
…
return address
buffercookie
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie_guess = [0x02, 0x01]send(buffer + cookie_guess)Connection+Server ok?
1
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
connect
proftpd libc stack
…
return address
buffer…
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie_guess = [0x02, 0x01, 0x42, …, 0x09]send(buffer + cookie_guess)Connection+Server ok?
1 9
√
√
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
connect
proftpd libc stack
…
return address
buffer…
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie = [0x02, 0x01, 0x42, …, 0x09] identified cookiesend(buffer + cookie)Connection+Server ok?
1 9
Saved rbx, etc.
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
connect
proftpd libc stack
…
return address
buffer…
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie = [0x02, 0x01, 0x42, …, 0x09]local_stack_guess = [0x00]send(buffer + cookie + local_stack_guess)Connection+Server ok?
1 9
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
connect
proftpd libc stack
…
return address
buffer…
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie = [0x02, 0x01, 0x42, …, 0x09]local_stack = [0x00, 0x00, …]return_address_guess = [0x00]send(buffer + cookie + local_stack + return_address_guess)Connection+Server ok?
1 9
0
Generalized Stack Reading
proftpd libc stack
…
return address
rsp cookie
Client
connect
proftpd libc stack
…
return address
buffer…
O v e r f l2
Waiting for clients…
buffer = “Overflow”cookie = [0x02, 0x01, 0x42, …, 0x09]local_stack = [0x00, 0x00, …]return_address = [0x7f, 0x3c, …]send(buffer + cookie + local_stack + return_address)Connection+Server ok?
1 9
0
Load address known! ROP chains!