LINUX
SOURCE
root@sre:~$ nano overwritingEIP.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int bowfunc(char *string) {
char buffer[1024];
strcpy(buffer, string);
return 1;
}
int main(int argc, char *argv[])
{
bowfunc(argv[1]);
printf("Done.\n");
return 1;
}root@sre:~$ sudo apt install gcc-multilib
root@sre:~$ gcc -m32 overwritingEIP.c -o overwritingEIP -fno-stack-protector -z execstack
STEP 1: ISR & BINARY COLLECTION
After performing target enumeration, the focus shifts to identifying whether the system is running outdated, misconfigured, or vulnerable software. Unlike relying solely on public exploits, this phase emphasizes gathering binaries or scripts directly from the target system for detailed offline analysis. Enumerating running processes and installed software is critical, as it helps uncover potential vulnerabilities that may not yet be publicly documented or require tailored exploitation. Tools such as ps aux, top, or htopon Linux, and tasklistor PowerShell cmdlets on Windows, can be used to list active processes and software versions. If source code is unavailable, reverse engineering the binary using tools like Ghidraor IDA Probecomes essential to understand its functionality and identify exploitable weaknesses. This step lays the groundwork for crafting a custom exploittailored to the specific target, rather than relying on generic public exploits.
CUSTOM EXPLOITS
PUBLIC EXPLOITS
With public exploits, the focus is on determining whether the target system is running software with known vulnerabilities, typically identified by their CVE (Common Vulnerabilities and Exposures) identifiers. Public exploit databases, such as Exploit-DB, Metasploit, or GitHub repositories, serve as valuable resources for locating pre-existing exploit code tailored to these vulnerabilities. However, public exploits often contain intentional errors or incomplete code as a security measure to prevent inexperienced users from directly executing them. This helps mitigate harm to individuals and organizations that may be affected by the vulnerability. To modify and adapt these public exploits, it is crucial to understand how the vulnerability works, identify the specific function or code segment where the vulnerability resides, and determine how to trigger its execution effectively. While public exploits save time and effort, they may require careful analysis and adjustments to adapt to the specific target environment, such as modifying payloads or bypassing basic security mechanisms. This method is particularly effective for quickly exploiting common vulnerabilities while ensuring responsible use.
STEP 2: IDENTIFY POTENTIALLY VULNERABLE FUNCTIONS OR UNSAFE CODING PRACTICES IN THE SOURCE
This is required to prevent blind testing with the objective of identifying unsafe functions or poor coding practices. When source code is available, review it directly for insecure functions (e.g., strcpy, gets, scanf without bounds). In the absence of source code, perform static analysis using tools such as Ghidra, IDA Pro, Binary Ninja, objdump -d, or radare2 to locate vulnerable functions, buffer handling routines, and unsafe coding patterns.
STEP 3: IDENTIFY THE PROGRAM ARCHITECTURE
This is essential as 32-bit and 64-bit offsets differ, and calling conventions/NX/ASLR behavior vary.
In 32-bit binaries saved return address is 4 bytes (EIP). The pattern/gdb approach maps 4-byte chunks directly.
In 64-bit binaries saved return address is 8 bytes (RIP). Tools must be aware of 8-byte alignment and you’ll typically use an 8-byte pattern or the same 4-byte pattern but interpret the registers appropriately. Also stack alignment, calling conventions, and NX/ASLR are stricter on x86_64 so you usually need ROP rather than raw shellcode injection.
STEP 4: TEMPORARILY DISABLE ASLR & SET DISPLAY SYNTAX
This ensures that memory addresses remain consistent across runs, providing a deterministic debugging environment and to accurately study the program's behavior.
STEP 5: FUZZING
Fuzzing is often the first step in binary vulnerability discovery. The goal is to supply malformed or unexpected inputs to the program’s parameters, arguments, or input channels (files, network packets, command-line arguments, environment variables, etc.) until the program crashes or behaves abnormally. A successful fuzzing campaign can reveal memory corruption, crashes, or hangs. These anomalies indicate potential vulnerabilities. If the crash occurs in a way that affects program control flow (for example, by overwriting the EIP register in 32-bit binaries), this strongly suggests the presence of a stack-based buffer overflow that may be exploitable.
COMON INPUT FIELDS FOR FUZZING

Prioritize input parameters that nominally accept short values (for example, date fields, country codes, single‑digit flags, or license/registration numbers) because developers often allocate small fixed buffers or implement brittle parsers for them. Also target fields that are passed into specific processing routines (e.g., license validation functions) or that are used after being opened (configuration files, playlist entries) as these are more likely to exercise parsing code and third‑party libraries where flaws reside. Because any program may expose many such parameters, focus first on those with the highest likelihood of overflow or parsing failures and fuzz them with oversized payloads, boundary numeric values, malformed formats, and non‑printable/binary data. Don’t forget to include larger free‑text fields, file uploads, and known risky file types (for example, certain audio or playlist formats - .wav or m3u files) in your scope since vulnerabilities often emerge in the libraries that handle those formats
LICENSE VALIDATION FIELDS
OPENED FILES
STEP 6: IDENTIFY EIP OFFSET
In a stack-based buffer overflow, the first objective is to determine the exact offset to EIP, which is the number of bytes required to overwrite the saved return address on the stack. This return address is automatically loaded into the EIP register when a function returns, so by overwriting it, an attacker can redirect the program’s execution flow. EIP itself is not directly writable, but it can be controlled indirectly by overflowing a buffer and reaching the location of the saved return address. To find this offset, a unique non-repeating input pattern is typically used to cause a controlled crash, and the value that ends up in EIP is analyzed to calculate how far into the input the overwrite occurred. Identifying this offset is critical in crafting a reliable exploit, as it allows precise placement of the address or instruction that will be executed when control is transferred.
CREATING UNIQUE NON-REPEATING PATTERNS:
METHOD 1: METASPLOIT
METHOD 2: ERC.XDBG PLUGIN
CREATE EXPLOIT
FUZZ APPLICATION
METHOD 1: VIA FIELDS
METHOD 2: VIA OPENED FILES

CALCULATE OFFSET
METHOD 1: METASPLOIT
METHOD 2: ERC.XDBG
CONTROL EIP

STEP 7: IDENTIFY USABLE BUFFER SPACE/PAYLOAD PLANNING
The goal of this step is to determine how much of the stack buffer can safely hold the payload. Even if the vulnerable buffer has a fixed size (e.g., 1024 bytes), factors such as stack layout, alignment, and surrounding function behavior can reduce usable space before causing crashes or corruption. Knowing the exact distance between the start of the buffer and the saved return address (EIP) is essential for designing payloads, especially when inserting shellcode that must fit within limited memory. Writing too much data can overwrite critical values, while too little may result in a failed exploit. Accurately calculating usable buffer space is critical for creating reliable and stable exploits.
CALCULATE SHELLCODE SIZE
ADJUSTMENT/ALIGNMENT VALUES

STEP 8: VERIFY PAYLOAD SPACE & OFFSET
This step tests and validates the buffer layout to ensure the shellcode can fit safely and that the calculated offsets align correctly with the saved return address (EIP). Instead of the actual shellcode, a test pattern is injected to confirm that the offsets, NOP sled, and shellcode placement are correct. This approach helps verify that there is sufficient usable buffer space and that the shellcode will execute reliably when inserted.
FORMULA

STEP 9: IDENTIFY BAD CHARACTERS
Bad characters can vary between programs and contexts because each program, library, protocol, or transport layer treats certain bytes specially. Some bytes terminate strings (for example, \x00 in C), others mark line endings (\x0A and \x0D), some are parsed as control or meta characters by shells, HTTP servers, or terminals, and intermediary systems such as proxies, CDNs, or libraries may normalize, filter, or reinterpret bytes through UTF-16 or Unicode handling, percent-encoding, or protocol framing. The exact input channel (argv, stdin, socket, or file), the APIs the target uses, any middleware, and protocol rules all influence which bytes are removed, transformed, or misinterpreted. Therefore always test for bad characters against the specific target and channel.

GENERATE CHARACTER LIST
RECALCULATE BUFFER
PERFORM DEBUGGING & ANALYSIS
IDENTIFY FIRST BAD CHARACTER
Analyze the stack & identify the first bad character. Look for the place where the CHARS start which should be after the 0x55s...
IDENTIFY SECOND BAD CHARACTER
Send payload without NULL bytes
CONTINUE IDENTIFYING BAD CHARACTERS UNTIL NONE LEFT
Send payload without "\x00" & "\x09"...repeat the process until all bad characters are identified and removed!
Continue remove bad characters until none are left
...
STEP 10: GENERATE SHELLCODE
This step is performed once all the BAD CHARACTERS are removed.
Before generating shellcode, it is essential to ensure that it matches the target system in several key areas. First, the architecture must correspond to the CPU type and instruction set of the target, such as x86, x86_64, or ARM, because shellcode is raw machine code and will not execute correctly on a mismatched architecture. Second, the platform must match the target operating system, such as Linux, Windows, or macOS, since system calls and API interfaces differ between OSes and shellcode is typically written to interact directly with these interfaces. Third, attention must be paid to bad characters, which are bytes that cannot appear in the shellcode because they may terminate strings, corrupt memory, or break input parsing; common examples include null bytes (\x00), carriage returns (\x0d), and newlines (\x0a). Ensuring alignment across these three areas—architecture, platform, and bad characters—is crucial for creating functional and reliable shellcode.
RECALCULATE PAYLOAD
VERIFY
STEP 11: IDENTIFY RETURN ADDRESS (STACK REDIRECTION)
The final step involves identifying the return address on the program stack, which will be used to overwrite the EIP and redirect execution to the desired location. This memory address must point to a valid instruction within the NOP sled or directly to the shellcode, ensuring the CPU executes the payload seamlessly. It is critical that the chosen address does not contain any bad characters (e.g., null bytes or other problematic values or previously found bad characters) that could corrupt the exploit or terminate the payload prematurely. Once the return address is correctly identified and the EIP is overwritten with it, the program will jump to the specified location, slide through the NOP sled (if applicable), and execute the shellcode, successfully triggering the exploit.

PAYLOAD FORMULA
VERIFICATION
EXPLOITATION
Revise the payload for exploitation over the network.
Last updated