Description
Fermat's last theorem meets format strings.
Setup
Connect to the challenge server with netcat.
Download the binary for local analysis.
nc <challenge_host> <PORT_FROM_INSTANCE>wget <challenge_url>/fermat-strings # binary for local analysisSolution
Walk me through it- Step 1Confirm the format string vulnerabilityConnect and send
%p.%p.%p.%pas input. If the server echoes back memory addresses instead of the literal string, you have a format string vulnerability - the input is passed directly to printf without a format string argument.bashecho '%p.%p.%p.%p' | nc <challenge_host> <PORT_FROM_INSTANCE>bashecho '%1$p.%2$p.%3$p' | nc <challenge_host> <PORT_FROM_INSTANCE>Learn more
A format string vulnerability arises when user-controlled input is passed as the format string to
printf,sprintf, or similar functions. Instead of safe code likeprintf("%s", user_input), the vulnerable code callsprintf(user_input)directly. This lets the attacker control what printf interprets as format specifiers.%pcauses printf to read a pointer-sized value from the stack and print it as a hex address. By sending many%pspecifiers, you can dump the entire printf argument list - which corresponds to consecutive stack words. This leaks stack data including saved return addresses, canary values, and libc pointers.Direct parameter access (
%N$p) lets you read the Nth argument directly without iterating:%7$preads the 7th stack word. This is useful for targeting a specific known offset, such as the stack canary position. - Step 2Leak the stack canary and libc addressUse
%N$pspecifiers with increasing N to dump stack words. The canary is recognizable - it ends in\x00and looks like0x00007f...XXXXXXXX. A libc address will be in the0x7f...range and offset from a known libc symbol.bash# Automate leak with pwntoolspythonpython3 -c "pythonfrom pwn import *bashp = remote('<host>', <PORT_FROM_INSTANCE>)bashp.sendline('%11$p.%15$p.%23$p') # adjust indicespythonprint(p.recvline())bash"Learn more
Stack canaries are random values placed between local variables and the saved return address by GCC when compiled with
-fstack-protector. Before the function returns, the canary is checked against its original value - if it changed (due to a stack overflow), the program aborts. Format string reads bypass this: you leak the canary value, then include the correct canary in your overflow payload so the check passes.To find which stack position holds the canary, look for a value that: (1) ends in a null byte (
\x00), (2) is 8 bytes wide, and (3) changes every run. The null byte is intentional - it terminates C strings and prevents the canary from being leaked by%s, though%pstill reads it. - Step 3Write to a target address using %nUse the
%nspecifier to write the number of characters printed so far into a target address on the stack. Craft a payload that positions the target address on the stack, then uses%Nc%offset$nto write the desired value byte by byte.bash# pwntools fmtstr_payload helperpythonpython3 -c "pythonfrom pwn import *bash# fmtstr_payload(offset, {target_addr: value_to_write})bashpayload = fmtstr_payload(6, {0x404080: 0xdeadbeef})pythonprint(payload)bash"Learn more
%nis the most dangerous printf specifier - it writes the count of characters printed so far to the address stored in the corresponding argument. By controlling the character count (via padding like%100c) and the address on the stack, an attacker can write arbitrary values to arbitrary memory locations.pwntools'
fmtstr_payload()automates this entirely. Given the offset (which stack position holds the first attacker-controlled word) and a dictionary of{address: value}, it generates the optimal format string. It uses%hhn(write 1 byte) to minimize the character count needed, writing one byte at a time.Common write targets: the GOT entry for
printfitself (overwrite withsystemso the nextprintf(input)callssystem(input)), or__free_hook, or the saved return address after leaking its position.
Flag
picoCTF{...}
Format string vulnerability - send %p specifiers to leak the stack canary and libc base, then use %n writes (or pwntools fmtstr_payload) to overwrite a GOT entry or function hook with system.