Attacking Binary Programs
Exploitation III
Gavin Lo, Tillson Galloway
Adapted from our lecture series from GreyHat at Georgia Tech
Exploitation
Reconnaissance
Enumeration
Exploitation
Post-Exploitation
Clean-up
How do we find potential vulnerabilities?
Once we have found vulnerabilities, how do we exploit them?
What is Binary?
Binary
0000
0001
0010
0011
0111
0
1
2
3
7
Binary (base 2) Decimal (base 10)
Negative numbers in binary
Naive implementation: use only the last bit
1001
1111
0000
1000
-1
-7
0
also 0?
Problem: how do we represent 0?
Two’s Complement
Use the last bit, but count differently
1001
1111
0000
1000
-7
-1
0
-8
Prevents “positive/negative 0”
Also allows you to add negative numbers for the correct result!
0000
Positive Numbers
1000
Negative Numbers
Warps Around!
Adding Two’s Complement Numbers
11111111 (-1) 11111111 (-1)
+11111111 (-1) +0000001 (+1)
_________ _________
111111110 10000000
Truncated to: Truncated to:
11111110 (-2) 0000000 (0)
Integer Overflow
Some programs parse two’s complement numbers without checking whether the number is too big, allowing you to give them negative numbers as input!
For a 32-bit number: E.G. (0000 0000 0000 0000 0000 0000 0000 0000), anything between 2^31 and 2^32-1 becomes a negative number because it changes the first bit!
Example
char inputString[16]; // create string of max length 16
gets(inputString); // ask for user input and store in inputString
int deposit = atoi(inputString); // convert string to integer
balance = balance + deposit; // update balance with deposit
Before the code executes
->
char inputString[16]; // create string of max length 16
gets(inputString); // ask for user input and store in inputString
int deposit = atoi(inputString); // convert string to integer
balance = balance + deposit; // update balance with deposit
00000000000000000000000000000
inputString
0000 (010)
deposit
0011 (310)
balance
Example
char inputString[16]; // create string of max length 16
-> gets(inputString); // ask for user input and store in inputString
int deposit = atoi(inputString); // convert string to integer
balance = balance + deposit; // update balance with deposit
110101010101010000000000000000
inputString
0000 (010)
deposit
0011 (310)
balance
Input: “6”
Example
char inputString[16]; // create string of max length 16
gets(inputString); // ask for user input and store in inputString
-> int deposit = atoi(inputString); // convert string to integer
balance = balance + deposit; // update balance with deposit
110101010101010000000000000000
inputString
0110 (610)
deposit
0011 (310)
balance
Input: “6”
Example
char inputString[16]; // create string of max length 16
gets(inputString); // ask for user input and store in inputString
int deposit = atoi(inputString); // convert string to integer
-> balance = balance + deposit; // update balance with deposit
110101010101010000000000000000
inputString
0110 (610)
deposit
1101 (-310)
balance
balance = 0110 + 0011
0110
1101
Negative!
balance = 3 + 6
Attacking Binary Programs
The Stack
0011
1001
1111
PUSH!
Memory
Program instructions
Stack
Registers
The Stack
PUSH!
Stack Frames
Variable
Variable
Saved RIP
Saved RBP
RSP (top of stack)
Memory
Program instructions
Stack
Variable
Variable
Saved RIP
Saved RBP
main()
main() {
puts(func1()); <-
}
func1() {
func2();
return “World!”;
}
func2() {
puts(“Hello!”);
}
Memory
Program instructions
Stack
Variable
Variable
Saved RIP
Saved RBP
Variable
Variable
Saved RIP
Saved RBP
main()
func1()
main() {
puts(func1()); <-
}
func1() {
func2(); <-
return “World!”;
}
func2() {
puts(“Hello!”);
}
Memory
Program instructions
Stack
Variable
Variable
Saved RIP
Saved RBP
Variable
Variable
Saved RIP
Saved RBP
main()
func1()
func2() - Stack Overflow!
main() {
puts(func1()); <-
}
func1() {
func2();
return “World!”; <-
}
func2() {
puts(“Hello!”); <-
}
Variable
Variable
Saved RIP
Saved RBP
Memory
Program instructions
Stack
Variable
Variable
Saved RIP
Saved RBP
Variable
Variable
Saved RIP
Saved RBP
main()
func1()
main() {
puts(func1()); <-
}
func1() {
func2(); <-
return “World!”;
}
func2() {
puts(“Hello!”);
}
Memory
Program instructions
Stack
Variable
Variable
Saved RIP
Saved RBP
Variable
Variable
Saved RIP
Saved RBP
main()
func1()
func2()
main() {
puts(func1()); <-
}
func1() {
func2(); <-
return “World!”;
}
func2() {
puts(“Hello!”); <-
}
Variable
Variable
Saved RIP
Saved RBP
Memory
Program instructions
Stack
Variable
Variable
Saved RIP
Saved RBP
Variable
Variable
Saved RIP
Saved RBP
main()
func1()
main() {
puts(func1()); <-
}
func1() {
func2();
return “World!”; <-
}
func2() {
puts(“Hello!”);
}
Memory
Program instructions
Stack
“World”
Variable
Saved RIP
Saved RBP
main()
main() {
puts(func1()); <-
}
func1() {
func2();
return “World!”;
}
func2() {
puts(“Hello!”);
}
Memory
Program instructions
Stack
“World”
Variable
Saved RIP
Saved RBP
0xc0
0xc4
0xc8
0xcc
main() {
puts(func1()); <-
}
func1() {
func2();
return “World!”;
}
func2() {
puts(“Hello!”);
}
Memory addresses
0x00
0x04
0x08
0x0c
0x08
0x0c
0xad
0xae
0xaf
0xbe
0xbc
Registers
Stack Frames
Variable
Variable
Saved RIP
Saved RBP
RSP (top of stack)
Arrays
array[1]
array[0]
array[2]
Base of Array
No Bounds Checking
RCX each time until it reaches zero
No Bounds Checking (cont)
Bounds checked program:
; Assume we have the length of the array stored in the memory address [length]�XOR RAX,RAX; Zero out RAX�.LOOP:� LEA RDX,[RBX+RCX*8]; Get address of our next thing to add� CMP RDX,0; Bounds check. Make sure it’s between 0 and length.� JL .ERROR;� CMP RDX,[length];� JGE .ERROR;� ADD RAX,[RDX]; Add the value� DEC RCX; Decrement RCX� TEST RCX,RCX; Test if RCX is 0. If not, repeat the loop.� JNZ .LOOP;
Buffer Overflows
array[7-15]
array[0-7]
Saved RIP
Saved RBP
RSP (top of stack)
Example
-> char inputString[8]; // create string of max length 16
gets(inputString); // ask for user input and store in inputString
int deposit = atoi(inputString); // convert string to integer
balance = balance + deposit; // update balance with deposit
RIP: address of the next instruction to be executed
****************************
inputString
0110 (610)
deposit
1101 (-310)
balance
****************************
Saved RIP register
“AAAAAAAAAAA” (len = 12)
Example
char inputString[8]; // create string of max length 16
-> gets(inputString); // ask for user input and store in inputString
int deposit = atoi(inputString); // convert string to integer
balance = balance + deposit; // update balance with deposit
RIP: address of the next instruction to be executed
1100110011001100110011001100
inputString
0110 (610)
deposit
1101 (-310)
balance
11001100110011000000********
Saved RIP register
“AAAAAAAAAAA” (len = 12)
Example
char inputString[8]; // create string of max length 16
gets(inputString); // ask for user input and store in inputString
-> int deposit = atoi(inputString); // convert string to integer
balance = balance + deposit; // update balance with deposit
RIP: address of the next instruction to be executed
1100110011001100110011001100
inputString
0110 (610 + 0)
deposit
1101 (-310)
balance
11001100110011000000
Saved RIP register
“AAAAAAAAAAA” (len = 12)
Example
char inputString[8]; // create string of max length 16
gets(inputString); // ask for user input and store in inputString
int deposit = atoi(inputString); // convert string to integer
-> balance = balance + deposit; // update balance with deposit
RIP: address of the next instruction to be executed
1100110011001100110011001100
inputString
0110 (610)
deposit
1101 (-310 + 0)
balance
11001100110011000000
Saved RIP register
“AAAAAAAAAAA” (len = 12)
Example
11001100110011000000 -> Could be anything, but probably outside bounds of program
RIP: address of the next instruction to be executed
11001100110011001100110011001100
inputString[8]
0110 (610)
deposit
1101 (-310)
balance
11001100110011000000
RIP register
“AAAAAAAAAAA” (len = 12)
“Segmentation fault!”
Example
1100
inputString[0]
0110
deposit
1101
balance
1100
inputString[1]
1100
inputString[2]
1100
inputString[3]
1100
inputString[4]
1100
inputString[5]
1100
inputString[6]
1100
inputString[7]
1100
RIP
1100
1100
char inputString[8];
int deposit
int balance
1100
How to exploit a buffer overflow
Demo: Buffer Overflows
Protection Mechanisms (Stack Carnies)
Data
Data
RBP
CANARY
RIP
Protection Mechanisms (Stack Carnies)
Shellcode
Shellcode
Shellcode
Shellcode
Pointer
Canary Modified! Program Crashes!
Protection Mechanisms (ASLR and DEP)
Useful Commands & Resources