OSED study materials
OSED study resources

OSED cheat sheet

OSED · Keep the key ideas within reach.

Quick reference

Reverse Engineering Binary Applications to Locate Vulnerabilities

  • Static analysis workflow: identify file type (file), check mitigations (checksec), list symbols/imports (nm, objdump -T, readelf -s), then disassemble/decompile (objdump -d, Ghidra, IDA).

  • Dangerous C functions to grep for in disassembly/strings: strcpy, strcat, sprintf, gets, scanf with %s, memcpy with user-controlled length, and any loop copying bytes without bounds check.

  • Stack frame layout rule: local buffers are at negative offsets from EBP/RBP; saved return address is at [EBP+4] (x86) or [RBP+8] (x64). Overflow distance = buffer offset to saved EIP/RIP.

  • Pattern to spot format string bugs: a printf-family call where the format argument is a register/stack value derived from user input rather than a .rodata string pointer.

  • Use cross-references (xrefs) in the decompiler to trace user input from recv/read/fgets to the vulnerable copy; note the exact byte count and destination size.

  • For indirect calls and virtual dispatch, resolve the vtable/function pointer table in .data.rel.ro or .rdata to map attacker-influenced control flow.

  • Dynamic confirmation: run under gdb with a cyclic pattern (e.g., pattern_create) and inspect EIP/RIP after crash to compute exact offset; verify with pattern_offset.

  • Compiler artifacts: stack canary is loaded from fs:0x28 (x64) or gs:0x14 (x86) at function prologue; its presence indicates __stack_chk_fail and a canary check before return.

Quick reference

Stack Buffer Overflow Exploitation

  • Offset formula: distance from start of overflowed buffer to saved return address = (saved EBP/RBP - buffer address) + pointer size (4 on x86, 8 on x64).

  • Classic x86 payload: [padding to EIP][address of shellcode][NOP sled][shellcode]; place shellcode after the return address to avoid clobbering it.

  • x64 calling convention: first six integer args in RDI, RSI, RDX, RCX, R8, R9; a ret2libc/ret2win chain must set these via gadgets before the call.

  • Stack alignment: x64 System V ABI requires RSP % 16 == 0 at the call instruction; add a single ret gadget before a function call if a movaps crash occurs.

  • ret2win: overwrite saved RIP with the address of an existing function (e.g., win/flag printer); no shellcode needed, bypasses NX.

  • ret2libc: chain = [pop rdi; ret][binsh_addr][system_addr] on x64; on x86 = [system_addr][fake_ret][binsh_addr]. Resolve libc base via leaked address minus known symbol offset.

  • Off-by-one/null-byte overflows can corrupt saved RBP low byte, enabling stack pivot via leave; ret when the saved frame pointer is attacker-controlled.

  • When the buffer is not adjacent to the return address (e.g., function pointers or adjacent structs), target the nearest control-data (function pointer, vtable) instead of EIP.

Quick reference

Custom Shellcode Development

  • Position-independent shellcode uses call/pop to obtain its own address: call $+5; pop esi (x86) or pop rsi (x64) to reference embedded strings.

  • Avoid null bytes: use xor eax,eax then mov al,imm; use push/pop sequences; test with msfvenom -b '\x00' or a custom encoder.

  • Linux x86 execve('/bin/sh',0,0): xor eax,eax; push eax; push 0x68732f2f; push 0x6e69622f; mov ebx,esp; xor ecx,ecx; xor edx,edx; mov al,11; int 0x80.

  • Linux x64 execve: xor rsi,rsi; push rsi; mov rbx,0x68732f2f6e69622f; push rbx; mov rdi,rsp; xor rdx,rdx; push 59; pop rax; syscall.

  • Windows x86 shellcode resolves API addresses via PEB->Ldr->InMemoryOrderModuleList, then walks the export table (IMAGE_EXPORT_DIRECTORY) to find functions by hash or name.

  • Staged shellcode: a small first stage reads a larger second stage into RWX memory (VirtualAlloc/mmap) and jumps to it; keeps initial payload under size limits.

  • Alphanumeric/ASCII-only encoders restrict bytes to [0-9A-Za-z]; use them when input filtering rejects non-alphanumeric bytes.

  • Test shellcode in isolation with a C harness that mmaps RWX memory, copies the bytes, and calls the buffer; verify no crashes before embedding in exploit.

Quick reference

Bypassing Modern Mitigations (DEP and ASLR)

  • DEP/NX marks stack and heap non-executable; bypass by ROP/JOP (reuse existing code), ret2libc, or mprotect/VirtualProtect to re-enable execute permission on a buffer.

  • ASLR randomizes base addresses of stack, heap, libraries, and PIE binaries; non-PIE main executable base is fixed, so its gadgets are usable without a leak.

  • Information leak primitives (format string %p/%s, uninitialized memory, out-of-bounds read) defeat ASLR by disclosing a libc or stack pointer; subtract known offset to get base.

  • Partial overwrite: overwrite only the low 1-2 bytes of a saved return address to redirect within the same 4KB/64KB region, bypassing ASLR without a leak.

  • ret2plt/ret2dlresolve: call functions via PLT stubs or resolve symbols at runtime, avoiding the need for a libc base leak.

  • Stack canary bypass: leak the canary via format string or OOB read, then include it in the payload; or overwrite a function pointer/return address not protected by the canary.

  • PIE binaries require a code leak to compute gadget addresses; if PIE is enabled but no leak exists, target non-PIE shared libraries or use partial overwrites.

  • Full RELRO makes GOT read-only; partial RELRO leaves .got.plt writable, enabling GOT overwrite as a control-flow hijack without stack overflow.

Quick reference

Format String Vulnerability Exploitation and Read Primitive Development

  • Format specifier %n writes the number of bytes printed so far to the address pointed to by the corresponding argument; %hn writes 2 bytes, %hhn writes 1 byte.

  • On x86, printf arguments beyond the format string are read from the stack; the Nth %p prints the Nth 4-byte stack slot after the format pointer.

  • Direct parameter access %N$p reads the Nth argument; %N$s dereferences it as a char* and prints the string at that address - a read primitive.

  • To place an address on the stack for %n, embed it in the format string itself; if the buffer is on the stack, the address occupies stack slots and can be referenced by index.

  • Write primitive: split target value into two 2-byte writes with %hn to avoid huge output; order writes by increasing value and account for bytes already printed.

  • Read primitive development: use %s with a pointer to a known address (e.g., GOT entry) to leak a libc address; compute libc base = leaked - symbol offset.

  • Precision: use %c or %d to control bytes printed before %n; for large widths, use %hn to limit output to 65535 bytes per write.

  • Finding your offset: send a pattern like AAAA%p.%p.%p... and locate 0x41414141 in the output; the index at which it appears is the stack argument position of your buffer.