An Introduction to Buffer Overflow Exploits Bart Coppens General Note This slide deck is based on a set of presentations I gave, so.

Download Report

Transcript An Introduction to Buffer Overflow Exploits Bart Coppens General Note This slide deck is based on a set of presentations I gave, so.

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 
We’ll be abusing vulnerabilities in code
Exploits 1:
Basic Buffer Overflows & Shellcode
Bart Coppens
A kind of vulnerability
The attack code we’ll
be 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 Exploits
x86-64 assembly crash course
A look at buffers are implemented in assembly
Buffer overflows
How to execute the attack code
Normal program behavior
callq
int function() {
return 0;
}
…
int i = function();
…
function
xor
retq
movq
%rax, …
%rax,%rax
Normal program behavior
callq
function
xor
retq
movq
Stack frame of main
Higher addresses
rsp
%rax, …
%rax,%rax
Normal program behavior
callq
function
xor
retq
movq
rsp
Return address to main
Higher addresses
Stack frame of main
%rax, …
%rax,%rax
Normal program behavior
callq
function
xor
retq
movq
Stack frame of main
Higher addresses
rsp
%rax, …
%rax,%rax
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
buffer[0]
rsp
0x64 bytes
buffer[99]
rsp
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rsp
rdi
0x64 bytes
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rsp
rdi
Return address etc
0x64 bytes
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rsp
Return address etc
H e l
rdi
l
…
0x64 bytes
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rsp
rdi
H e l
l
…
0x64 bytes
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rsp
rdi
H e l
l
…
0x64 bytes
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rdi
rsp
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rdi
rsp
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(50, "Hello\n");
}
rsp
H e l
rdi
l
…
0x64 bytes
buffer[99]
buffer[100]
buffer[107]
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "Hello…ABCDEFGH");
}
rsp
H e l
rdi
l
…
0x64 bytes
buffer[99]
buffer[100]
buffer[107]
adresses
Return address to main
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "Hello…ABCDEFGH");
}
rsp
H e l
rdi
l
…
0x64 bytes
buffer[99]
buffer[100]
buffer[107]
adresses
A B C D E F G H
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "Hello…ABCDEFGH");
}
…
rdi
rsp
adresses
A B C D E F G H
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "Hello…ABCDEFGH");
}
…
rdi
rsp
adresses
A B C D E F G H
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "Hello…ABCDEFGH");
}
rsp
rdi
adresses
A B C D E F G H
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…ABCDEFGH");
}
rsp
rdi
movq $0, %rax
…
syscall
adresses
A B C D E F G H
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…ABCDEFGH");
}
0x123
rsp
rdi
movq $0, %rax
…
syscall
adresses
A B C D E F G H
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
rsp
rdi
movq $0, %rax
…
syscall
0
adresses
0 0 0 0 1 2 3
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
rdi
movq $0, %rax
…
syscall
rsp
0
adresses
0 0 0 0 1 2 3
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Local variables and the stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
rdi
movq $0, %rax
…
syscall
rsp
0
adresses
0 0 0 0 1 2 3
Stack frame of main
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
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 
Exploits part 2
Buffer Overflows:
Mitigations & Advanced Exploits
Bart Coppens
Overview
•
•
•
•
•
•
Recap
Non-executable stack & ROP
Function pointers
Address Space Layout Randomization
Stack reading
Hands-on!
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
rsp
buffer
adresses
return address
Stack of ‘main’
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
rsp
movq $0, %rax
…
syscall
0
3
adresses
0 0 0 0 1 2
Stack of ‘main’
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
movq $0, %rax
…
syscall
rsp
0
3
adresses
0 0 0 0 1 2
Stack of ‘main’
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
movq $0, %rax
…
syscall
Stack of ‘main’
…
adresses
rsp
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
movq $0, %rax
…
syscall
adresses
variable
size!
Stack of ‘main’
…
… environment …
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x123
movq $0, %rax
…
syscall
0
adresses
variable
size!
0 0 0 0 1 2 3
Stack of ‘main’
…
… environment …
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x12F
rsp
movq $0, %rax
…
syscall
0
adresses
variable
size!
0 0 0 0 1 2 3
Stack of ‘main’
…
… environment …
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
0x12F
movq $0, %rax
…
syscall
rsp
0 0 0 0 1 2 3
Stack of ‘main’
…
… environment …
adresses
variable
size!
0
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
(garbage instructions)
0x12F
movq $0, %rax
…
syscall
rsp
0 0 0 0 1 2 3
Stack of ‘main’
…
… environment …
adresses
variable
size!
0
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
rsp
adresses
variable
size!
nop
nop NOP slide/sled
nop
movq $0, %rax
…
0 0 0 0 0 1 2 3
Stack of ‘main’
…
… environment …
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
nop
nop NOP slide/sled
nop
movq $0, %rax
…
variable
size!
Stack of ‘main’
…
… environment …
adresses
rsp
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
nop
nop NOP slide/sled
nop
movq $0, %rax
…
variable
size!
Stack of ‘main’
…
… environment …
adresses
rsp
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Executable stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
nop
nop NOP slide/sled
nop
movq $0, %rax
…
variable
size!
Stack of ‘main’
…
… environment …
adresses
rsp
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Non-Executable Stack
int silly_function(int len, char* src) {
char buffer[100];
memcpy(buffer, src, len);
function(%rdi, %rsi, %rdx)
return 0;
}
int main() {
return silly_function(108, "\x48\xc7…230100…");
}
movq $0, %rax
…
syscall
Stack of ‘main’
…
adresses
rsp
Stack memory mapped
as non-executable!
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Non-Executable Stack
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
movq $0, %rax
…
syscall
Stack of ‘main’
…
adresses
rsp
4K
R,X
4K
R,X
4K 4K
R,W R,W
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Non-Executable Stack
proftpd
libc
stack
0
0x7ff…
4K
R,X
rsp
0
4K
R,X
4K 4K
R,X R,W
3
adresses
0 0 0 0 1 2
Stack of ‘main’
…
4K
R,X
4K
R,X
4K 4K
R,W R,W
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
rsp
0
3
adresses
0 0 0 0 1 2
Stack of ‘main’
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
rsp
adresses
address of exit
Stack of ‘main’
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
rsp
adresses
address of system
Stack of ‘main’
…
sub
movq
movq
callq
xor
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
%rax,%rax
$0x64, %rsp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
esp
32 bit
adresses
address of system
Stack of ‘main’
…
…
xor
add
ret
%eax,%eax
$0x64, %esp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
esp
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
address of system
garbage (‘return addr.’)
argument 1
argument 2
32 bit
adresses
argument 3
Stack of ‘main’
…
…
xor
add
ret
%eax,%eax
$0x64, %esp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
esp
address of system
garbage (‘return addr.’)
argument 1
argument 2
32 bit
adresses
argument 3
Stack of ‘main’
…
…
xor
add
ret
%eax,%eax
$0x64, %esp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
address of system
esp
garbage (‘return addr.’)
argument 1
argument 2
32 bit
adresses
argument 3
Stack of ‘main’
…
…
xor
add
ret
%eax,%eax
$0x64, %esp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
address of system
esp
garbage (‘return addr.’)
argument 1
argument 2
32 bit
adresses
argument 3
Stack of ‘main’
…
…
xor
add
ret
%eax,%eax
$0x64, %esp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
address of system
esp
garbage (‘return addr.’)
ptr. to “/bin/sh”
argument 2
32 bit
adresses
argument 3
Stack of ‘main’
…
…
xor
add
ret
%eax,%eax
$0x64, %esp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
address of system
esp
can be used to execute
a second function!
garbage (‘return addr.’)
ptr. to “/bin/sh”
argument 2
32 bit
adresses
argument 3
Stack of ‘main’
…
…
xor
add
ret
%eax,%eax
$0x64, %esp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
address of system
rsp
fun(%rdi,%rsi,%rdx)
garbage (‘return addr.’)
ptr. to “/bin/sh”
argument 2
64 bit
adresses
argument 3
Stack of ‘main’
…
…
xor
add
ret
%rax,%rax
$0x64, %rsp
Return-to-libc
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
rsp
fun(%rdi,%rsi,%rdx)
64 bit
adresses
Stack of ‘main’
…
xor
add
ret
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
rsp
fun(%rdi,%rsi,%rdx)
64 bit
adresses
Stack of ‘main’
…
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
rsp
&(pop-rdi; ret)
fun(%rdi,%rsi,%rdx)
ptr. to “/bin/sh”
address of system
64 bit
adresses
Stack of ‘main’
…
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
&(pop-rdi; ret)
rsp
fun(%rdi,%rsi,%rdx)
ptr. to “/bin/sh”
address of system
64 bit
adresses
Stack of ‘main’
…
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
&(pop-rdi; ret)
fun(%rdi,%rsi,%rdx)
ptr. to “/bin/sh”
rsp
address of system
64 bit
adresses
Stack of ‘main’
…
“/bin/sh”
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
&(pop-rdi; ret)
fun(%rdi,%rsi,%rdx)
ptr. to “/bin/sh”
address of system
“/bin/sh”
rsp
64 bit
adresses
Stack of ‘main’
…
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
&(pop-rdi; ret)
fun(%rdi,%rsi,%rdx)
ptr. to “/bin/sh”
address of system
“/bin/sh”
rsp
64 bit
adresses
Stack of ‘main’
…
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
&(pop-rdi; ret)
fun(%rdi,%rsi,%rdx)
0x20
64 bit
adresses
…
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
Arguments in registers!
chaining gadgets
&(pop-rdi; ret)
fun(%rdi,%rsi,%rdx)
0x20
&(pop rsi;pop rdx;ret)
0x23
0xFF73
64 bit
adresses
…
…
pop
ret
…
xor
add
ret
%rdi
gadget
%rax,%rax
$0x64, %rsp
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
chaining gadgets
&(pop-rdi; ret)
0x20
0x81
&(pop rsi;pop rdx;ret)
0x23
0xFF73
64 bit
adresses
…
0xfa
0x5c
0xc3
0x00
0x00
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
cmp $0xc35c, %edx
chaining gadgets
&(pop-rdi; ret)
0x20
0x81
&(pop rsi;pop rdx;ret)
0x23
0xFF73
64 bit
adresses
…
0xfa
0x5c
0xc3
0x00
0x00
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
cmp $0xc35c, %edx
chaining gadgets
&(pop-rdi; ret)
0x20
0x81
0xfa
0x5c
0xc3
&(pop rsi;pop rdx;ret)
pop %rsp
0x23
0xFF73
64 bit
adresses
…
0x00
0x00
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
cmp $0xc35c, %edx
chaining gadgets
&(pop-rdi; ret)
0x20
0x81
0xfa
0x5c
0xc3
&(pop rsi;pop rdx;ret)
ret
0x23
0xFF73
64 bit
adresses
…
0x00
0x00
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
cmp $0xc35c, %edx
chaining gadgets
&(pop-rdi; ret)
0x20
0x81
0xfa
0x5c
0xc3
&(pop rsi;pop rdx;ret)
pop %rsp; ret
0x23
0xFF73
64 bit
adresses
…
0x00
0x00
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
• Pop-gadgets
• Write-anywhere gadgets (mov %rdi, (%rsi); ret)
• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)
• Usually it can be a simple stager:
• Write shellcode in RW memory (read)
chaining gadgets
&(pop-rdi; ret)
0x20
&(pop rsi;pop rdx;ret)
0x23
0xFF73
64 bit
adresses
…
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K 4K
R,X R,W
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
chaining gadgets
&(pop-rdi; ret)
0x20
&(pop rsi;pop rdx;ret)
0x23
0xFF73
64 bit
adresses
…
• Pop-gadgets
• Write-anywhere gadgets (mov %rdi, (%rsi); ret)
• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)
• Usually it can be a simple stager:
• Write shellcode in RW memory (read)
• Make page executable (mprotect)
(not possible with PaX)
Return-Oriented Programming
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K
R,X
4K
R,X
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
chaining gadgets
&(pop-rdi; ret)
0x20
&(pop rsi;pop rdx;ret)
0x23
0xFF73
64 bit
adresses
…
• Pop-gadgets
• Write-anywhere gadgets (mov %rdi, (%rsi); ret)
• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)
• Usually 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
proftpd
libc
stack
0
0x7ff…
4K
R,X
4K
R,X
4K
R,X
4K
R,X
4K
R,X
4K
R,X
4K 4K
R,W R,W
exit, read, write,
system, mprotect, …
overflown buffer
chaining gadgets
&(pop-rdi; ret)
0x20
&(pop rsi;pop rdx;ret)
0x23
0xFF73
64 bit
adresses
…
• Pop-gadgets
• Write-anywhere gadgets (mov %rdi, (%rsi); ret)
• Turing-complete set of gadgets for complex
shellcode (eventually calling libc/kernel)
• Usually 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 pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
pos
Animal* a = …;
a->isCuddly();
…
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
Dog::isCute
isCute
isCuddly
Labrador::isCuddly
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
Dog::isCute
isCute
isCuddly
Labrador::isCuddly
system
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
Dog::isCute
isCute
isCuddly
isCute
isCuddly
Labrador::isCuddly
system
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
Dog::isCute
isCute
isCuddly
isCute
isCuddly
Labrador::isCuddly
system
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
rsp
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
Labrador::isCuddly
pop …
ret
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
rsp
ret. addr call
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
Labrador::isCuddly
pop …
ret
Not under our control!
No ROP possible?
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
rsp
ret. addr call
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
rdi
Some buffer
Labrador::isCuddly
pop …
ret
normal
ROP
chain
…
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
rsp
ret. addr call
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
rdi
Some buffer
Labrador::isCuddly
mov %rdi, %rsp
ret
normal
ROP
chain
…
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
rsp
ret. addr call
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
rdi
Some buffer
rsp
Labrador::isCuddly
mov %rdi, %rsp
ret
normal
ROP
chain
…
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
ret. addr call
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
rdi
Some buffer
rsp
Labrador::isCuddly
mov %rdi, %rsp
ret
normal
ROP
chain
…
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
ret. addr call
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
rdi
Some buffer
rsp
Labrador::isCuddly
mov %rdi, %rsp
ret
normal
ROP
chain
…
Overwriting function pointers
struct Animal {
virtual bool isCute();
virtual bool isCuddly();
Position pos;
};
vtable
Animal* a = …;
pos
a->isCuddly();
…
fake vtable
movq (…), %rax
movq 0x8(%rax), %rax
callq *%rax
ret. addr call
local vars
ret addr. main
…
Dog::isCute
isCute
isCuddly
isCute
isCuddly
rdi
Some buffer
rsp
Labrador::isCuddly
xchg %rdi, %rsp
ret
normal
ROP
chain
…
Function pointers to libraries
proftpd
libc
exit
+0x78230
stack
Function pointers to libraries
proftpd
libc
stack
exit
+0xA8230
Function pointers to libraries
NVidia? Intel? AMD?
Pillars of Eternity
+ ???
libGL
libm
libc
stack
Function pointers to libraries
proftpd
libc
write
+???
stack
Function pointers to libraries
libc
data
code
call write
write
proftpd
stack
Function pointers to libraries
libc
data
PLT
code
call write@PLT
write@PLT:
write
proftpd
stack
Function pointers to libraries
libc
PLT
code
call write@PLT
write
proftpd
stack
write@PLT:
jmp GOT[_write]
data
GOT
symbol resolver stub
Function pointers to libraries
libc
PLT
code
call write@PLT
write
proftpd
stack
write@PLT:
jmp GOT[_write]
data
GOT
symbol resolver stub
Function pointers to libraries
libc
PLT
code
call write@PLT
write
proftpd
stack
write@PLT:
jmp GOT[_write]
data
GOT
symbol resolver stub
all function pointers in the Global Offset Table
can be overwritten, next time application calls
write, attack code is executed
Use full RELRO relocations: GOT completely filled out
on program startup and remapped read-only
Address Space Layout Randomization
proftpd
rsp
adresses
address of exit
Stack of ‘main’
…
libc
stack
Address Space Layout Randomization
proftpd
rsp
adresses
address of exit
Stack of ‘main’
…
libc
stack
Address Space Layout Randomization
proftpd
libc
stack
Randomize on each program start:
• Load address of libraries
• Stack base
• Heap base
rsp
adresses
address of exit
Stack of ‘main’
…
Address Space Layout Randomization
proftpd
libc
stack
32 bit
…
page offset
>=4 bits
<=16 bits
Brute-forcable
12 bits
64 bit
canonical address
16 bits
…
page offset
>=28 bits
Brute forcing harder
12 bits
Address Space Layout Randomization
proftpd
rsp
adresses
address of exit
Stack of ‘main’
…
libc
stack
Address Space Layout Randomization
proftpd
libc
write@PLT
At fixed address!
rsp
gadgets in proftpd
Stack of ‘main’
…
stack
Address Space Layout Randomization
proftpd
libc
stack
Only possible with Position Independent Executable (PIE)
(binary can act like a library)
rsp
gadgets in proftpd
Stack of ‘main’
…
Stack canary/cookie
buffer
H e l
l
…
0x64 bytes
buffer[99]
return address
buffer[107]
sub
movq
movq
callq
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
$0x64, %rsp
Stack Smash Protector / Cookies
buffer
H e l
l
…
0x64 bytes
stack cookie/canary
return address
sub
movq
movq
callq
add
retq
$0x64, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
$0x64, %rsp
Stack Smash Protector / Cookies
buffer
H e l
l
…
0x64 bytes
stack cookie/canary
return address
sub
movq
movq
callq
add
retq
$0x6C, %rsp
%rdi, %rdx
%rsp, %rdi
<memcpy>
$0x6C, %rsp
Stack Smash Protector / Cookies
Per-process value initialized by loader
Segment-relative addressing
hard to leak value
buffer
H e l
l
…
0x64 bytes
stack cookie/canary
return address
sub
mov
mov
movq
movq
callq
add
retq
$0x6C, %rsp
%fs:0x28,%rax
%rax,0x64(%rsp)
%rdi, %rdx
%rsp, %rdi
<memcpy>
$0x6C, %rsp
Stack Smash Protector / Cookies
Per-process value initialized by loader
Segment-relative addressing
hard to leak value
buffer
H e l
l
…
0x64 bytes
stack cookie/canary
return address
sub
mov
mov
movq
movq
callq
mov
xor
jne
add
retq
$0x6C, %rsp
%fs:0x28,%rax
%rax,0x64(%rsp)
%rdi, %rdx
%rsp, %rdi
<memcpy>
0x64(%rsp),%rax
%fs:0x28,%rax
… stack_chk_fail
$0x6C, %rsp
Stack Smash Protector / Cookies
Per-process value initialized by loader
Segment-relative addressing
hard to leak value
(argument copies)
local variables
buffer
H e l
l
…
0x64 bytes
stack cookie/canary
saved rbx, …
return address
sub
mov
mov
movq
movq
callq
mov
xor
jne
add
retq
$0x6C, %rsp
%fs:0x28,%rax
%rax,0x64(%rsp)
%rdi, %rdx
%rsp, %rdi
<memcpy>
0x64(%rsp),%rax
%fs:0x28,%rax
… stack_chk_fail
$0x6C, %rsp
Information leakage
Unknown to attacker:
• Stack/code addresses
• Cookie
Leak 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
Waiting for clients…
rsp
Client
cookie
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
accept() connection
fork()
rsp
cookie
return address
Client
…
proftpd
libc
Clone of the parent process:
Same addresses, same cookie!
stack
rsp
cookie
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
cookie
rsp
return address
Client
…
√
proftpd
libc
stack
O v e r f l
buffer
buffer = “Overflow”
√
cookie
return address
send(buffer)
Connection+Server ok?
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
cookie
rsp
return address
Client
…
proftpd
libc
stack
buffer
buffer = “Overflow”
cookie_guess = [0x00]
send(buffer + cookie_guess)
Connection+Server ok?
O v e r f l
0
cookie
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
Client
cookie
return address
…
buffer = “Overflow”
cookie_guess = [0x00]
send(buffer + cookie_guess)
Connection+Server ok?
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
cookie
rsp
return address
Client
…
proftpd
libc
stack
buffer
buffer = “Overflow”
cookie_guess = [0x01]
send(buffer + cookie_guess)
Connection+Server ok?
O v e r f l
1
cookie
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
cookie
rsp
return address
Client
…
√
proftpd
libc
stack
buffer
buffer = “Overflow”
cookie_guess = [0x02]
send(buffer + cookie_guess)
Connection+Server ok!
O v e r f l
√ 2
cookie
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
cookie
return address
Client
…
proftpd
libc
stack
buffer
buffer = “Overflow”
cookie_guess = [0x02, 0x00]
send(buffer + cookie_guess)
Connection+Server ok?
O v e r f l
2 0 cookie
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
cookie
return address
Client
…
proftpd
libc
stack
buffer
buffer = “Overflow”
cookie_guess = [0x02, 0x01]
send(buffer + cookie_guess)
Connection+Server ok?
O v e r f l
2 1 cookie
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
cookie
return address
Client
…
√
proftpd
libc
stack
buffer
buffer = “Overflow”
cookie_guess = [0x02, 0x01, 0x42, …, 0x09]
send(buffer + cookie_guess)
Connection+Server ok?
O v e r f l
9
√ 2 1 …
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
cookie
return address
Client
…
proftpd
libc
stack
buffer
buffer = “Overflow”
cookie = [0x02, 0x01, 0x42, …, 0x09] identified cookie
send(buffer + cookie)
Saved rbx, etc.
Connection+Server ok?
O v e r f l
9
2 1 …
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
cookie
return address
Client
…
proftpd
buffer = “Overflow”
cookie = [0x02, 0x01, 0x42, …, 0x09]
local_stack_guess = [0x00]
send(buffer + cookie + local_stack_guess)
Connection+Server ok?
libc
stack
buffer
O v e r f l
9
2 1 …
return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
cookie
return address
Client
…
proftpd
libc
buffer = “Overflow”
buffer
cookie = [0x02, 0x01, 0x42, …, 0x09]
local_stack = [0x00, 0x00, …]
return_address_guess = [0x00]
send(buffer + cookie + local_stack + return_address_guess)
Connection+Server ok?
stack
O v e r f l
9
2 1 …
0return address
…
Generalized Stack Reading
proftpd
libc
stack
Waiting for clients…
rsp
cookie
return address
Client
Load address known!
proftpd
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?
ROP chains!
libc
…
stack
buffer
O v e r f l
9
2 1 …
0return address
…
Some other exploitation techniques
• JIT spraying
• Heap spray, heap grooming/feng shui
• Buffers in statically allocated sections (data,
BSS, etc)
• …
Some other vulnerabilities to exploit
•
•
•
•
•
•
•
•
•
Exploiting double free/use after free/etc
Heap spray, heap grooming/feng shui
Type confusion
Races
Integer overflows
Your regular kind of vulnerability, but in the kernel
The sky is the limit…
Have a look at googleprojectzero.blogspot.com
Have a look at The Art of Software Security Assessment