Local Target

Published: March 5, 2024

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.

Buffer overflow practiceDownload local-target

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-target
wget https://artifacts.picoctf.net/c/519/local-target.c
cat local-target.c

Solution

  1. Step 1Measure the offset
    num 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 by int num = 64. On the stack, buf sits at a lower address and num immediately follows at a higher address. Writing exactly 24 bytes fills buf without touching num; writing 25 bytes overwrites the first byte of num.

    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.

  2. Step 2Overflow by one byte
    Adding 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 64108
    Learn more

    The byte 'A' has ASCII value 65 (0x41). The variable num is initialized to 64 (0x40). Since x86 is little-endian, the least significant byte of num is 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 of num, changing it from 0x40 to 0x41 (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 between strlen() (excludes null terminator) and sizeof() (includes null), can create exactly this type of overflow.

    The payload here uses python3 -c 'print(...)' piped into nc (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, and process() for local testing.

  3. Step 3Capture the flag output
    Once num == 65, the binary congratulates you and prints the picoCTF flag directly.
    Learn more

    When the overflow successfully sets num to 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.

Want more picoGym Exclusive writeups?

Useful tools for Binary Exploitation

Related reading

What to try next