Description
Overflow the stack buffer so that the neighboring local variable num becomes 65. Once num holds the magic value the binary prints the flag.
Setup
Grab both the binary and its source to understand how the 24-byte buffer and num variable sit in memory.
Run it locally to test candidate payload lengths before attacking the remote instance.
wget https://artifacts.picoctf.net/c/519/local-targetwget https://artifacts.picoctf.net/c/519/local-target.ccat local-target.cSolution
- Step 1Measure the offsetnum is stored immediately after the 24-byte input buffer. Feeding exactly 24 characters leaves num at its initialized value (64).
Learn more
A stack buffer overflow occurs when more data is written to a fixed-size buffer than it can hold. The excess bytes spill into adjacent memory, overwriting whatever variables happen to be stored there. On the stack, local variables are allocated in a predictable order (though compilers may reorder them or add padding), so overflowing one variable can reliably reach and overwrite a neighboring one.
In this binary, the source code declares something like
char buf[24]followed byint num = 64. On the stack,bufsits at a lower address andnumimmediately follows at a higher address. Writing exactly 24 bytes fillsbufwithout touchingnum; writing 25 bytes overwrites the first byte ofnum.Reading the source code (when available) is always the first step. The source reveals the buffer size, the target variable, its initial value, and the win condition. With source code, you can calculate the exact offset mathematically rather than guessing. Without source code, you would use GDB to map the stack layout or use a cyclic pattern (e.g., from pwntools'
cyclic()) to determine offsets experimentally. - Step 2Overflow by one byteAdding a single extra byte overwrites the low byte of num. Writing 'A' bumps it from 64 (0x40) to 65 (0x41), which satisfies the win condition.
python3 - <<'PY' print('12345678901234567890123AA') PY | nc saturn.picoctf.net 64108Learn more
The byte
'A'has ASCII value 65 (0x41). The variablenumis initialized to 64 (0x40). Since x86 is little-endian, the least significant byte ofnumis stored at the lowest address - immediately adjacent to the end of the buffer. Writing one extra byte past the buffer writes directly into that low byte ofnum, changing it from0x40to0x41(i.e., from 64 to 65).This is a classic example of a one-byte overflow or off-by-one overflow. In real-world vulnerabilities, off-by-one errors in bounds checking are surprisingly common and have led to serious exploits. The difference between
<=and<in a length check, or betweenstrlen()(excludes null terminator) andsizeof()(includes null), can create exactly this type of overflow.The payload here uses
python3 -c 'print(...)'piped intonc(netcat) to send data to the remote service. Netcat is the standard tool for sending raw data to TCP services in CTF exploitation. For more complex exploits, pwntools (a Python library) provides a much richer API:p32()/p64()for packing integers into bytes,remote()for connections, andprocess()for local testing. - Step 3Capture the flag outputOnce num == 65, the binary congratulates you and prints the picoCTF flag directly.
Learn more
When the overflow successfully sets
numto 65, the program's conditional check (if (num == 65)) passes and it prints the flag. This demonstrates the core principle of buffer overflow exploitation: corrupting program state to reach code paths that were not intended to be accessible with normal input.Local Target is a deliberately simple example of this class of vulnerability. Real-world stack overflows target the return address - the address on the stack that the function will jump to when it returns. By overwriting the return address with the address of a useful function (like a
win()function that the developer left in the binary) or with shellcode, an attacker can gain arbitrary code execution. Protections like stack canaries, ASLR, and non-executable stacks (NX/DEP) exist specifically to make this harder.The picoGym buffer overflow series (local-target, buffer-overflow-0, buffer-overflow-1, buffer-overflow-2, buffer-overflow-3) progressively removes training wheels: first you overwrite a neighbor variable, then a return address in a binary without protections, then with increasing numbers of mitigations active. This progression mirrors the actual learning path for binary exploitation and pwn CTF categories.
Flag
picoCTF{l0c4l5_1n_5c0p...8441a}
Any payload that increases num from 0x40 to 0x41 will work; the example string above is just one convenient option.