> For the complete documentation index, see [llms.txt](https://www.ctfrecipes.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-modification.md).

# Data modification

Luckily C contains a rarely-used [format specifier `%n`](/pwn/stack-exploitation/format-string/specifier.md#type-field). This specifier takes in a pointer (memory address) and writes there **the&#x20;*****number of characters written so far***.&#x20;

{% hint style="success" %}
If the input can be controlled, the number of characters written and the location where they are written can also be controlled.
{% endhint %}

## How %n works ?&#x20;

As explain in introduction, `%n` take a pointer as argument and write there **the&#x20;*****number of characters written so far***.&#x20;

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.

{% hint style="warning" %}
Note that the `%n` specifier will write an int size data at the targeted location so it will write "0x00000041" instead of "0x41"&#x20;

use :&#x20;

* `%hhn` to write a char size data (1 byte)
* `%hn` to write a short size data (2 bytes)
  {% endhint %}

## 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 ?&#x20;

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](/pwn/stack-exploitation/format-string/data-leak.md#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. &#x20;

{% hint style="warning" %}
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
{% endhint %}

Finally there is the `%n` or any of variants.&#x20;

{% hint style="warning" %}
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.&#x20;

So, if the chosed value is 0x44434241, the payload must be :&#x20;

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

\
16953 = 16961 (0x4241) - 8&#x20;

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.&#x20;
{% endhint %}

## Pwntools

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

```python
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 :&#x20;

```python
from pwn import *
payload = fmtstr_payload(7, {0x41414141 : 0x44434241})
```

```
b'%65c%18$hhn%1c%19$hhn%1c%20$hhn%1c%21$hhnaaaAAAABAAACAAADAAA'
```

{% hint style="info" %}
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 )&#x20;
{% endhint %}
