Skip to main content

buffer overflow 0 picoCTF 2022 Solution

Overflow a small buffer in a 32-bit binary to trigger an alternate code path that reveals the flag.

Published: July 20, 2023Updated: August 13, 2026

Description

A simple C program reads user input into a fixed-size buffer on the stack without bounds checking. Overflow the buffer to trigger SIGSEGV - the signal handler for the crash prints the flag.

This is the most introductory buffer overflow challenge in picoCTF 2022, requiring no return-address control - just enough bytes to crash the program.

Connect to the challenge server via netcat. No local binary is required.

Send more than 16 bytes of input to overflow the stack buffer.

bash
nc saturn.picoctf.net <PORT_FROM_INSTANCE>
python
python3 -c "print('A'*100)" | nc saturn.picoctf.net <PORT_FROM_INSTANCE>

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
New to binary exploitation? Buffer Overflow and Binary Exploitation for CTF covers stack layout, ret2win, format strings, heap exploitation, and PIE bypass.
  1. Step 1Understand the vulnerability
    Observation
    The description says input is read into a fixed-size buffer with no bounds checking. That is a classic C stack overflow, rooted in an unsafe function like gets() or strcpy().
    The program uses gets() or a similarly unsafe function to fill a fixed-size buffer. No bounds check means any input larger than the buffer overflows adjacent stack space.
    Learn more

    Buffer overflows are the classic memory-corruption vulnerability. When a program copies user input into a stack-allocated array without checking the length, bytes beyond the array boundary overwrite adjacent memory - saved frame pointers, return addresses, and local variables of calling functions.

    The C functions gets(), strcpy(), and sprintf() are all unsafe because they perform no length validation. Modern C code should use fgets(buf, sizeof(buf), stdin) or strncpy() instead. The gets() function was deprecated in C99 and removed entirely in C11 for this reason.

    When overflowed memory contains critical control-flow data (like a saved return address), the CPU attempts to jump to a garbage address, triggering a SIGSEGV (segmentation fault). In this challenge, the SIGSEGV handler is deliberately set to print the flag - so triggering the crash is sufficient.

  2. Step 2Send the overflow payload
    Observation
    The buffer is 16 bytes and nothing mentions a canary. Piping a large blob of repeated bytes over netcat, well past 16, will clobber the saved return address and trigger the SIGSEGV handler.
    Pipe ~100 bytes of 'A' into the program. The buffer is 16 bytes; anything past it spills into adjacent stack data and eventually corrupts the saved return address.
    python
    python3 -c "print('A'*100)" | nc saturn.picoctf.net <PORT_FROM_INSTANCE>
    python
    python3 -c "import sys; sys.stdout.buffer.write(b'A'*100)" | nc saturn.picoctf.net <PORT_FROM_INSTANCE>

    Expected output

    picoCTF{ov3rfl0ws_ar3_ez_56...}
    What didn't work first

    Tried: Sending exactly 16 bytes (the declared buffer size) to fill it completely.

    Fill the buffer exactly and the saved return address survives, so the program returns cleanly and the handler never fires. You need enough bytes to spill past the buffer and into the adjacent stack data. Anything above 17 starts corrupting the frame, and a margin like 100 guarantees the return address itself is hit.

    Tried: Typing 'A's interactively into nc instead of piping them.

    Typing interactively sends a line only when you press Enter, so the server reads your input one buffered line at a time, the newline eats one of your bytes, and the read loop may process the input before enough has arrived. Piping a pre-built payload delivers every byte in one write, which is how overflow input reaches a remote service reliably.

    Learn more

    The exact overflow size is layout-dependent: how big the buffer is, what local variables sit between it and the saved frame pointer, whether a stack canary is present, and so on. 100 bytes is a deliberately oversized blunt-force value. Anything >= 32 bytes will overrun this particular buffer; you want enough to clobber the return address regardless of layout.

    If the program is whitespace-sensitive, print()'s trailing newline can become a problem. Two trailing-newline-free alternatives: python3 -c "import sys; sys.stdout.buffer.write(b'A'*100)" or echo -n. The buffer.write form also avoids any encoding surprises.

    Despite the description hinting at NX, NX (checksec --file will confirm) doesn't apply here: you're not executing your input as code. The flag is printed by the SIGSEGV handler, which runs after the crash but before the process exits. NX would only matter if you were trying to jump into your buffer.

  3. Step 3Read the flag from the SIGSEGV handler output
    Observation
    The description says the crash handler prints the flag. So once the overflow raises SIGSEGV, the flag appears on stdout with no further exploitation.
    The challenge process registers a SIGSEGV handler that prints the flag and exits. Triggering the crash is the entire exploit.
    Learn more

    A signal handler is a function registered with signal(SIGSEGV, handler) or sigaction. When the kernel raises SIGSEGV (memory access violation), execution jumps into the handler before the process is killed. The challenge author wired the handler to print the flag - so a clean fault is the win.

    In real exploitation this is the opposite of normal: signal handlers usually fight the exploit by catching the crash, logging it, and restarting the process. The challenge inverts that for didactic effect.

Interactive tools
  • Cyclic Pattern GeneratorGenerate de Bruijn cyclic patterns and find buffer overflow offsets. The browser equivalent of pwntools cyclic and cyclic_find.
  • pwntools Payload BuilderPack integers into little-endian bytes (p32 / p64), unpack bytes back to integers, and build flat ROP payloads with offset-based insertion.

Flag

Reveal flag

picoCTF{ov3rfl0ws_ar3_ez_56...}

Send more than 16 bytes to crash the program; the SIGSEGV handler prints the flag automatically.

Key takeaway

Stack buffer overflows happen when a program writes user-controlled data into a fixed-size array without checking the length, so bytes past the boundary corrupt adjacent stack memory. Unsafe C functions like gets() and strcpy() are the canonical source, which is why they were deprecated or dropped from the standard. The same root cause turns up wherever C and C++ are written without bounds-checking wrappers: network daemons, embedded firmware, browser engines, and OS kernels have all shipped critical CVEs from this one pattern.

Related reading

Tools used in this challenge

Where to go next