Practical Examples

Prog 1

//classic/prog_1.c
//gcc –O0 –fno-stack-protector –o prog_1 prog_1.c

#include <stdio.h>

int main() {
    char username[32];
    puts("username:");
    gets(username);
    printf("Welcome %s!\n", username);
    return 0;
}

Description

  • Reads the username from the command line.

  • Input is stored in the variable username.

  • The variable can hold strings up to 31 characters.

  • gets function has no limit on input size.

  • printf will print the content.

Shows a simple write beyond boundaries.

  • printf also shows a read beyond boundaries.

Reading more than 31 characters will result in overwriting the memory after the username.

  • There are no other variables, so this will be stack structures (addressed later).

printf will print characters up to 0x00 potentially printing program memory.

  • The function is insecure as no explicit boundaries exist except the actual string content.

Exercise

  1. Install: pip3 install --user gdb-gef

  2. Compile the binary: gcc -g -O0 -fno-stack-protector -o prog_1 prog_1.c

  3. Analyze the execution with different payloads.

    1. Print register: p $rsp or variable address p &username.

    2. Check the stack information: info frame.

  4. Determine:

    1. What is the stack base address?

    2. Where is the return information?

    3. How many bytes can be entered without overflow?

    4. How many bytes can be written without damage?

    5. What happens when an overflow is achieved?

What is the stack base address?

  • info frame: 0x7ffffffedf50

  • p $rbp: 0x7ffffffedf40

Where is the return information?

  • Just before $rbp

How many bytes can be entered without overflow?

  • sizeof(username) - 1

How many bytes can be written without damage?

  • 32

  • It could have been different due to empty space.

What happens when an overflow is achieved?

  • Saved $BP is overwritten and then saved $PC is overwritten.

  • In this case, 31 'a's were provided and an additional '\0' was added at 0x...edf38

Prog 2

int main() {
    char allowed = 0;
    char password[8];
    char username[8];
    char message[32];
    
    puts("username:");
    gets(username);
    puts("password:");
    gets(password);
    allowed = strcmp("admin", username) + \
        strcmp("topsecrt", password);
    
    puts("message:");
    gets(message);
    
    printf("user=%s pass=%s result=%d\n", username, \
        password, allowed);
        
    if(allowed == 0)
        printf("Access granted. Message sent!\n");
    else
        printf("Access denied\n");
    
    return 0;
}

Flow:

  • Asks for username and password;

  • Validates credentials;

  • Asks for message;

  • If the user is authenticated, access is granted;

Issues:

  • Several uncontrolled reads;

  • All variables may overwrite each other.

Demonstrate overwrite of local variables.

  • Each vulnerable variable may overwrite others above.

Variable order will determine how it can be exploited.

  • Implementation dependent.

message is the prime suspect as it is written after the evaluation is done.

It can also change an internal decision (flow inside the function) by writing over the allowed variable.

Exercise

  1. Compile the binary: gcc -g -O0 -fno-stack-protector –o prog_2 prog_2.c

  2. Analyze the execution with different payloads.

    1. Print register: p $rsp or variable address p &username.

    2. Check the stack information: info frame

  3. Determine

    1. What is the stack base address?

    2. Where is the return information?

    3. How many bytes can be entered into the message without overflow?

    4. How many bytes can be written without damage?

    5. What happens when an overflow is achieved?

    6. How can the decision be subverted?

Last updated