Please sit on the right half of the room
--->
20 minutes
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
Buffer Overflows
Dangling Pointers
Integer Overflows/Underflows
Format String Vulnerabilities
printf
).void vulnerable_function(char *input) {
char buffer[8];
strcpy(buffer, input);
}
int main(int argc, char *argv[]) {
vulnerable_function(argv[1]);
return 0;
}
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?
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");
}
}
... |
Lower addrs
Higher addrs
return addr
base addr
computed_hash[16]
stored_hash[16]
buffer[64]
Most modern systems are little-endian as opposed to big-endian
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 |
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");
}
}
... |
Lower addrs
Higher addrs
return addr
base addr
computed_hash[16]
stored_hash[16]
buffer[64]
Redirect execution to the input buffer
padding | return addr | nop | shellcode |
---|---|---|---|
"A" * 64 + 32 + 8 | "\x00\x00\x00... | "\x90" * <size> | "\x48\x31\xf6..." |
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");
}
}
... |
Lower addrs
Higher addrs
return addr
base addr
computed_hash[16]
stored_hash[16]
buffer[64]
canary
Address Space Layout Randomization
Data Execution Prevention