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.

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?