Data modification

Luckily C contains a rarely-used format specifier %n. 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 Arbitrary data read.

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 )

Last updated