DEV Community

Suprim Devkota
Suprim Devkota

Posted on

Exploit Development: Buffer Overflows

Introduction

Let’s say you have a candy jar with a capacity for 10 candies. Each candy represents a piece of data. Now, let’s say you have a friend who loves to share candies with you.

In a world without proper checks, your friend might get excited and decide to share 15 candies with you, not realizing the jar can only hold 10. As a result, candies spill out of the jar, making a mess. In the realm of computer security, this overflow of candies is similar to a buffer overflow, where data spills beyond the allocated space, potentially causing chaos and security vulnerabilities.

To understand buffer overflows we have to first look at the anatomy of memory in the computer and the organization of stack in said memory.

Anatomy of Memory

A buffer overflow attack is exploited when the data sent to the buffer overflows the Buffer Space into the Extended Instruction Pointer (EIP) which contains the address of the next instruction to be executed. Changing the value in this EIP register to point to malicious code is the crux of the whole exploit.

Steps involved in a buffer overflow attack

  1. Finding the vulnerable service by spiking: Spiking involves sending various input values to the target service to identify potential vulnerabilities. This helps in understanding how the service behaves under different conditions and if it reacts unexpectedly to certain inputs. Identifying a service that exhibits abnormal behavior or crashes in response to specific inputs might indicate a potential buffer overflow vulnerability.

  2. Fuzz the service: Fuzzing is the process of sending a large number of random or specially crafted inputs to the target service to observe its response. This helps in discovering points of failure or unexpected behavior. Fuzzing aims to trigger any unexpected responses or crashes that may indicate the presence of a buffer overflow vulnerability.

  3. Obtain the offset address of the EIP: Once a buffer overflow is identified, the next step is to determine the offset at which the Extended Instruction Pointer (EIP) is overwritten. This involves sending input with a pattern and analyzing the crash to find the distance from the start of the buffer to the overwritten EIP.Purpose: Knowing the offset is crucial for crafting the payload to precisely overwrite the EIP.

  4. Overwrite the EIP: Craft a payload to overwrite the EIP with a controlled value, typically pointing to the location of the attacker’s shellcode. By controlling the EIP, the attacker can redirect the program’s execution to the injected malicious code instead of following the normal flow.

  5. Find bad characters: Identify characters that may interfere with the proper execution of the payload, such as null bytes (/x00) or other characters that might be altered during transmission. This ensures that the payload doesn’t contain characters that could disrupt the execution of the exploit.

  6. Find the right module: Identify the module or library in the target process that can be used to execute the payload. This often involves locating a module with a memory address that remains consistent across different executions of the program. Determining a reliable module helps in crafting the exploit to ensure stability and effectiveness.

  7. Generate Shellcode: Create a payload, often in the form of shellcode, which is a small piece of code that performs a specific action, such as spawning a shell or providing remote access. The shellcode is the actual payload that the attacker wants to execute on the target system.

  8. Gain Root!: Execute the crafted exploit to take control of the target system, typically by escalating privileges to gain root access or the highest level of control. This is the ultimate objective, allowing the attacker to manipulate the system as desired.

Buffer Overflow Safeguards and Prevention

Runtime bounds checking makes it theoretically impossible for a buffer overflow to happen by checking whether the space in a buffer is enough to accomodate the incoming data. This is the reason why languages such as Python and Java implement it.

However there are also performance costs associated with this method as extra computation has to be done every time an element is to be inserted. This is the reason why languages such as C and C++ do not implement it and hence why programs written in these languages are vulnerable to overflows.

Top comments (0)