OSED study materials
OSED study resources

OSED: Format string exploitation and read primitive development

OSED · Work through your exam, one topic at a time.

All guide sections

Format string exploitation and read primitive development

Format string exploitation and read primitive development

When user input is passed directly as the format string to printf, sprintf, _snprintf, or vsprintf, the attacker owns both a read primitive and a write primitive. This domain tests building a reliable read first - leaking stack contents and module pointers - and only then converting that into control.

Core rules

  • On x86 cdecl, variadic arguments sit on the stack above the format pointer; %x prints them in order and positional specifiers such as %6$x index a slot directly.
  • To read arbitrary memory: place the target address at the front of the buffer, find which argument slot points back into that buffer, and reference it with %s. %s dereferences; %x does not - a crash on %s usually means the pointer you supplied is wrong, which is itself information about alignment.
  • The write primitive uses %n/%hn; split the target dword into two halves and write each with a separate address (target, target+2) to keep the character count manageable.
  • On Windows, the MSVC CRT disables %n by default from VS2005 onward, so confirm the runtime before planning a write. Also distinguish ANSI from wide (wsprintfW) paths.

Worked example

Send AAAA%x.%x.%x.%x.%x.%x. The output eventually contains 41414141, meaning the buffer's first dword appears at that argument index. If it is the seventh, then %7$s treats the bytes at the start of the buffer as a pointer and prints the string there. Replace AAAA with the little-endian address of a saved return address or a module pointer, and you have an ASLR-breaking read that feeds directly into a DEP bypass chain.

Traps that cost marks

  • Miscounting slots, because the format string's own buffer address occupies one of them.
  • Target addresses containing null bytes, truncating the string before the specifier.
  • Misaligning the address block relative to the argument grid, so a slot reads half of one address and half of another.
  • Planning a %n write against a CRT that has the conversion disabled.