🏳️
The CTF Recipes
  • Introduction
  • Cryptography
    • Introduction
    • General knowledge
      • Encoding
        • Character encoding
          • ASCII
          • Unicode
          • UTF-8
        • Data encoding
          • Base16
          • Base32
          • Base64
      • Maths
        • Modular arithmetic
          • Greatest Common Divisor
          • Fermat's little theorem
          • Quadratic residues
          • Tonelli-Shanks
          • Chinese Remainder Theorem
          • Modular binomial
      • Padding
        • PKCS#7
    • Misc
      • XOR
    • Mono-alphabetic substitution
      • Index of coincidence
      • frequency analysis
      • Well known algorithms
        • 🔴Scytale
        • 🔴ROT
        • 🔴Polybe
        • 🔴Vigenere
        • 🔴Pigpen cipher
        • 🔴Affine cipher
    • Symmetric Cryptography
      • AES
        • Block Encryption procedure
          • Byte Substitution
          • Shift Row
          • Mix Column
          • Add Key
          • Key Expansion / Key Schedule
        • Mode of Operation
          • ECB
            • Block shuffling
              • Challenge example
            • ECB Oracle
              • Challenge example
          • CBC
            • Bit flipping
              • Challenge example
            • Padding oracle
              • Challenge example
          • OFB
            • Key stream reconstruction
            • Encrypt to Uncrypt
  • 🛠️Pwn
    • General knowledge
      • STACK
        • Variables storage
        • Stack frame
      • PLT and GOT
      • HEAP
        • HEAP operations
        • Chunk
        • Bins
        • Chunk allocation and reallocation
      • Syscall
    • Architectures
      • aarch32
        • Registers
        • Instruction set
        • Calling convention
      • aarch64
        • Registers
        • Instruction set
        • Calling convention
      • mips32
        • Registers
        • Instruction set
        • Calling convention
      • mips64
        • Registers
        • Instruction set
        • Calling convention
      • x86 / x64
        • Registers
        • Instruction set
        • Calling convention
    • Stack exploitation
      • Stack Buffer Overflow
        • Dangerous functions
          • gets
          • memcpy
          • sprintf
          • strcat
          • strcpy
        • Basics
          • Challenge example
        • Instruction pointer Overwrite
          • Challenge example
        • De Bruijn Sequences
        • Stack reading
          • Challenge example
      • Format string
        • Dangerous functions
          • printf
          • fprintf
        • Placeholder
        • Data Leak
          • Challenge example
        • Data modification
          • Challenge example
      • Arbitrary code execution
        • Shellcode
        • ret2reg
        • Code reuse attack
          • Ret2plt
          • Ret2dlresolve
          • GOT Overwrite
          • Ret2LibC
          • Leaking LibC
          • Ret2csu
          • Return Oriented Programming - ROP
          • Sigreturn Oriented Programming - SROP
          • Blind Return Oriented Programming - BROP
            • Challenge example
          • 🔴Call Oriented Programming - COP
          • 🔴Jump Oriented Programming - JOP
          • One gadget
        • Stack pivoting
    • 🛠️Heap exploitation
      • Heap overflow
        • Challenge example
      • Use after free
        • Challenge example
      • 🛠️Double free
      • 🔴Unlink exploit
    • Protections
      • Stack Canaries
      • No eXecute
      • PIE
      • ASLR
      • RELRO
    • Integer overflow
Powered by GitBook
On this page
  • How %n works ?
  • Padding
  • Arbitrary write
  • Pwntools
  1. Pwn
  2. Stack exploitation
  3. Format string

Data modification

PreviousChallenge exampleNextChallenge example

Last updated 2 years ago

Luckily C contains a rarely-used . This specifier takes in a pointer (memory address) and writes there the number of characters written so far.

If the input can be controlled, the number of characters written and the location where they are written can also be controlled.

How %n works ?

As explain in introduction, %n take a pointer as argument and write there the number of characters written so far.

This mean that to write 0x41 which is 81 in decimal, It means that 81 characters must be written before the %n placeholder in order to make it write 0x41 at the targeted location.

Note that the %n specifier will write an int size data at the targeted location so it will write "0x00000041" instead of "0x41"

use :

  • %hhn to write a char size data (1 byte)

  • %hn to write a short size data (2 bytes)

Padding

As explained before, a large amount of chars had to be sent in order to write a byte. But in much case, the user input lenght is size limited, so how to write as many characters as needed ?

To do that, the usage of padding will help : %81d will write 81 characters (the decimal value of the pointed argument padded with spaces in order to have a data size of 81)."

Arbitrary write

In order to write at an arbitrary location, the user input offset must be known because the targeted address will be injected here, such as .

Then, A format string will be sent that will write the sufficient amount of characters in order to write the chosen value.

If the chosen data is 0x41, the format string need to write 77 chars (81-4) because there is already 4 written char to inject the targeted address

Finally there is the %n or any of variants.

In order to write an arbitrary value larger than 2 bytes, it's generaly needed to write it with several %hn format string cause that write more than 0xffff (65535) chars in one time is too long.

So, if the chosed value is 0x44434241, the payload must be :

[targeted address][targeted address+2]%16953d%7$hn%514d%6$hn

16953 = 16961 (0x4241) - 8

514 = 17475 (0x4443) - 16961 (chars already written for the first arbitrary write)

Note that the lowest value must be write first because it's impossible to padd with negative values.

Pwntools

Pwntools has a feature for automating %n format string exploits:

from pwn import *
payload = fmtstr_payload(offset, {location : value})

Example if the user input offset is 7 and the targeted address is 0x41414141 and the arbitrary chose value is 0x44434241 :

from pwn import *
payload = fmtstr_payload(7, {0x41414141 : 0x44434241})
b'%65c%18$hhn%1c%19$hhn%1c%20$hhn%1c%21$hhnaaaAAAABAAACAAADAAA'

In this example, the format string is injected before the targeted address ( it's better to do that in case of null bytes in the targeted address that will stop the printf function )

🛠️
format specifier %n
Arbitrary data read