Week 5
Please sit on the right half of the room
--->
Quiz
20 minutes
Vulnerability Analysis
What is Vulnerability Analysis?
Why focus on C programs?
-
C is widely used in systems programming
-
Can be vulnerable to various types of security flaws, including buffer overflows and improper pointer usage.
-
Understanding vulnerabilities in C helps developers write more secure code and defend against common attacks.
Vulnerability analysis involves identifying weaknesses in software that can be exploited by attackers to gain unauthorized access or execute malicious code
Common Vulnerabilities
-
Buffer Overflows
- Occurs when data exceeds the allocated buffer size and overwrites adjacent memory.
- Can lead to crashing the program, modifying control flow, or executing arbitrary code.
-
Dangling Pointers
- Accessing memory after it has been freed or deallocated.
- May cause unpredictable behavior or allow attackers to manipulate memory contents.
-
Integer Overflows/Underflows
- Arithmetic operations result in values that exceed the storage capacity of a variable.
- Can lead to incorrect calculations or potential bypass of security checks.
-
Format String Vulnerabilities
- Incorrect usage of format specifiers (e.g., in
printf
). - Can lead to reading or writing unintended memory locations, allowing attackers to disclose sensitive information or modify data.
- Incorrect usage of format specifiers (e.g., in
Minimal Example
void vulnerable_function(char *input) {
char buffer[8];
strcpy(buffer, input);
}
int main(int argc, char *argv[]) {
vulnerable_function(argv[1]);
return 0;
}
Questions?
Lab 1
(Part 1: Vulnerability Analysis)
Lab Questions
- What kind of file is it?
- Does it link or import any recognizable cryptographic libraries?
- Which functions specifically?
- How is the password being checked?
- Can the password be extracted from the executable?
unsigned char correct_hash[16] = "\xe5\xe9\xfa\x1b\x9b\xa0\x88\x0f\x48\x63\x8b\xb4\xef\xee\xa7\x1a";
void vulnerable_function() {
char buffer[64];
unsigned char stored_hash[16];
unsigned char computed_hash[16];
memset(buffer, 0, sizeof(buffer));
memcpy(stored_hash, correct_hash, 16);
printf("Enter input: ");
// Read from user input
fgets(buffer, 256, stdin);
// Compute MD5 hash of user input
compute_md5(buffer, computed_hash);
// Print the computed and stored hash before comparison
print_hash("Computed MD5 Hash", computed_hash, 16);
print_hash("Stored MD5 Hash (on stack)", stored_hash, 16);
// Compare computed hash with stored hash (stack value)
if (memcmp(computed_hash, stored_hash, 16) == 0) {
printf("Hash match. Access granted!\n");
} else {
printf("Hash mismatch. Access denied.\n");
}
}
Where's the vulnerability? What variables can I overwrite?
Lab 1
(Part 2: Password Bypass)
unsigned char correct_hash[16] = "\xe5\xe9\xfa\x1b\x9b\xa0\x88\x0f\x48\x63\x8b\xb4\xef\xee\xa7\x1a";
void vulnerable_function() {
unsigned char computed_hash[16];
unsigned char stored_hash[16];
char buffer[64];
memset(buffer, 0, sizeof(buffer));
memcpy(stored_hash, correct_hash, 16);
printf("Enter input: ");
// Vulnerable: reads more than buffer size
fgets(buffer, 256, stdin);
// Compute MD5 hash of user input
compute_md5(buffer, computed_hash);
// Print the computed and stored hash before comparison
print_hash("Computed MD5 Hash", computed_hash, 16);
print_hash("Stored MD5 Hash (on stack)", stored_hash, 16);
// Compare computed hash with stored hash (stack value)
if (memcmp(computed_hash, stored_hash, 16) == 0) {
printf("Hash match. Access granted!\n");
} else {
printf("Hash mismatch. Access denied.\n");
}
}
... |
Stack
Lower addrs
Higher addrs
- If I write to memory sequentially, which direction will it overwrite the table?
- Which of these variables can I control with an overflow?
return addr
base addr
computed_hash[16]
stored_hash[16]
buffer[64]
Questions?
Before the next lab...
Most modern systems are little-endian as opposed to big-endian
Example
When you write an address to the stack in the next lab, you'll need to convert it to little-endian.
Address | Big-Endian | Little Endian |
---|---|---|
0x1234567890ABCDEF | 12 34 56 78 90 AB CD EF | EF CD AB 90 78 56 34 12 |
0x0011223344556677 | 00 11 22 33 44 55 66 77 | 77 66 55 44 33 22 11 00 |
Lab 2
(Part 1: Control Flow Hijacking)
unsigned char correct_hash[16] = "\xe5\xe9\xfa\x1b\x9b\xa0\x88\x0f\x48\x63\x8b\xb4\xef\xee\xa7\x1a";
void vulnerable_function() {
unsigned char computed_hash[16];
unsigned char stored_hash[16];
char buffer[64];
memset(buffer, 0, sizeof(buffer));
memcpy(stored_hash, correct_hash, 16);
printf("Enter input: ");
// Vulnerable: reads more than buffer size
fgets(buffer, 256, stdin);
// Compute MD5 hash of user input
compute_md5(buffer, computed_hash);
// Print the computed and stored hash before comparison
print_hash("Computed MD5 Hash", computed_hash, 16);
print_hash("Stored MD5 Hash (on stack)", stored_hash, 16);
// Compare computed hash with stored hash (stack value)
if (memcmp(computed_hash, stored_hash, 16) == 0) {
printf("Hash match. Access granted!\n");
} else {
printf("Hash mismatch. Access denied.\n");
}
}
... |
Stack
Lower addrs
Higher addrs
return addr
base addr
computed_hash[16]
stored_hash[16]
buffer[64]
- How big should our overwrite buffer be?
- Where else could we redirect execution?
Arbitrary Code Execution
Redirect execution to the input buffer
- Fill the input buffer with assembly instructions
- The hex representation of assembly is called shellcode
- Redirect execution to our input buffer
- Pad it with nop (no operation) instructions if we want extra leeway in the location of our jump
- Find a /bin/sh execution online
- New buffer:
padding | return addr | nop | shellcode |
---|---|---|---|
"A" * 64 + 32 + 8 | "\x00\x00\x00... | "\x90" * <size> | "\x48\x31\xf6..." |
Lab 2
(Part 2: Exploitation)
Mitigations
- Stack canaries
- Data Execution Prevention
- Address Space Layout Randomization
unsigned char correct_hash[16] = "\xe5\xe9\xfa\x1b\x9b\xa0\x88\x0f\x48\x63\x8b\xb4\xef\xee\xa7\x1a";
void vulnerable_function() {
unsigned char computed_hash[16];
unsigned char stored_hash[16];
char buffer[64];
memset(buffer, 0, sizeof(buffer));
memcpy(stored_hash, correct_hash, 16);
printf("Enter input: ");
// Vulnerable: reads more than buffer size
fgets(buffer, 256, stdin);
// Compute MD5 hash of user input
compute_md5(buffer, computed_hash);
// Print the computed and stored hash before comparison
print_hash("Computed MD5 Hash", computed_hash, 16);
print_hash("Stored MD5 Hash (on stack)", stored_hash, 16);
// Compare computed hash with stored hash (stack value)
if (memcmp(computed_hash, stored_hash, 16) == 0) {
printf("Hash match. Access granted!\n");
} else {
printf("Hash mismatch. Access denied.\n");
}
}
... |
Stack
Lower addrs
Higher addrs
return addr
base addr
computed_hash[16]
stored_hash[16]
buffer[64]
canary
Stack Canaries
- Checked to see if altered before function return
ASLR
Address Space Layout Randomization
- Randomizes the locations of stack, heap, libraries, executable code, etc
- Difficult for the attacker to guess where to return
DEP
Data Execution Prevention
- Make stack not executable
- If we can't write executable instructions to the stack, instead we'll write a bunch of return addresses to little snippets of assembly already inside the program (gadgets)
- Your homework will be to bypass it by building a ROP chain
Questions?
Week 5
By Chase Kanipe
Week 5
- 21