Format String Vulnerbilities

clgp

2025/11/16

Categories: ctf Tags: pwn

fmtstr_payload() in pwntools

from pwn import *

payload = fmtstr_payload(offset, writes, numbwritten='byte')

+offset is the stack offset of where you want to do the payload

+writes is a pair value { address_to_write_to: value_to_write }

+numbwritten (optional) is the number of byte that have been printed before the payload is processed

Example

Let’s say you are doing a CTF challenge where:

You found a format string vulnerability.

You used a debugger (like GDB) and found that your payload starts at offset 7.

You want to overwrite a variable called is_admin located at address 0x0804a02c with the value 1 to get admin privileges.

from pwn import *

# The address of the 'is_admin' variable
admin_variable_address = 0x0804a02c

# The value we want to write (1)
new_value = 1

# The stack offset we found
offset = 7

# Generate the payload
payload = fmtstr_payload(offset, {admin_variable_address: new_value})


# You would then send this payload to the vulnerable program
# p = process('./vulnerable_program')
# p.sendline(payload)

When the vulnerable program executes printf(payload), this specially crafted string will cause it to write the value 1 into the address 0x0804a02c. fmtstr_payload() automatically handles all the %n, %hn, %hhn specifiers and padding (%…c) to get the exact value you want written to the exact address.

Why this function fmtstr_payload() actually help

This function help yout to write payload which control exactly what value gets written to which address.

 Why This Is a Lifesaver: The Manual Method

Our Goal: Write the 4-byte value 0xDEADBEEF to the address 0x12345678.

Assumptions:

  • We are on a little-endian system (most x86/ARM CPUs).
  • This means 0xDEADBEEF at 0x12345678 is stored in memory as:     0xEF at 0x1234567B     0xBE at 0x1234567A     0xAD at 0x12345679     0xDE at 0x12345678
  • We will use %hhn (which writes 1 byte) for our writes.
  • The printf byte counter (which %hhn uses) starts at 0.

Step 0: The Sort

The printf counter can only increase. We can’t write 222 bytes and then write 173 bytes, because the counter would already be past 173.

Therefore, we must sort our writes from the lowest byte value to the highest:

  1.  Write 0xAD (173) -> to address 0x12345679
  2.  Write 0xBE (190) -> to address 0x1234567A
  3.  Write 0xDE (222) -> to address 0x12345678
  4.  Write 0xEF (239) -> to address 0x1234567B

Our payload will perform 4 writes in this specific order.


Step 1: Write the value 173 (0xAD)

Step 2: Write the value 190 (0xBE)

Step 3: Write the value 222 (0xDE)

Step 4: Write the value 239 (0xEF)


Summary

After 4 complex, ordered steps, we have performed 4 writes:

All 4 of these bytes combine to form the value 0xDEADBEEF at the address 0x12345678.

This is the extremely complex logic (sorting, calculating differences, handling padding, and managing addresses) that fmtstr_payload() automates for you in a single, clean line of code.

>> Home