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.
Setup
Connect to the challenge server via netcat. No local binary is required.
Send more than 16 bytes of input to overflow the stack buffer.
nc saturn.picoctf.net <PORT_FROM_INSTANCE>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.
Step 1Understand the vulnerability
ObservationThe 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(), andsprintf()are all unsafe because they perform no length validation. Modern C code should usefgets(buf, sizeof(buf), stdin)orstrncpy()instead. Thegets()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.
Step 2Send the overflow payload
ObservationThe 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.pythonpython3 -c "print('A'*100)" | nc saturn.picoctf.net <PORT_FROM_INSTANCE>pythonpython3 -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)"orecho -n. Thebuffer.writeform also avoids any encoding surprises.Despite the description hinting at NX, NX (
checksec --filewill 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.Step 3Read the flag from the SIGSEGV handler output
ObservationThe 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)orsigaction. 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.