Description
A netcat binary lets you write and read back numbered entries. The read path parses your entry number with strtol, which returns 0 for non-numeric input. There is a hidden branch that prints the flag when the entry number is 0, but the read code only runs once at least one entry exists. So: create one dummy entry, then read with a non-numeric value to land on entry 0 and dump the flag.
Setup
Launch a challenge instance from the picoCTF web panel; it shows the host and a port shown here as <PORT_FROM_INSTANCE>.
Connect with netcat and read the provided source. Note the read path uses strtol and a special entry_number == 0 branch.
Explore the menu: option 1 writes an entry, option 2 reads one back.
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 1Read the source and find the strtol quirk
ObservationThe challenge ships its source, and the description mentions strtol. So read the source for the exact path where strtol's return value becomes an array index without anyone checking the endptr.The source ships with the challenge. The read handler does something like entry_number = strtol(input, ...) and then has a branch: if entry_number == 0, print the flag. strtol returns 0 when the input has no leading digits, so any non-numeric input selects entry 0. But the read handler is only reachable after at least one entry has been created, so you must add a dummy entry first.bash# Read the source: confirm strtol on the read input and an if (entry_number == 0) flag branch.bash# Confirm the read path requires num_entries >= 1.What didn't work first
Tried: Try reading entry 0 immediately without writing any entry first.
The read handler checks the entry count before it looks at the entry number at all, so it returns early with a no-entries message and the strtol trick never fires. Create at least one dummy entry to make the read path reachable.
Tried: Assume the flag branch is triggered by passing the literal string '0' as the entry number.
'0' is a valid numeric string, so strtol returns 0 exactly as it would for 'a'. The point is that it returns 0 for completely non-numeric input too. The challenge is built around that path, so passing '0' works while missing the actual bug class, the unchecked endptr, which the source review exists to surface.
Learn more
strtolparses an optional sign and leading digits and returns 0 if there are none (e.g. the input"a"). Many programs treat that 0 as a valid index. Here entry 0 is the sentinel that prints the flag, so the bug is feeding the parser something it silently turns into 0, not any memory corruption.Step 2Create one dummy entry, then read a non-numeric value
ObservationThe read handler sits behind a check that at least one entry exists, and strtol returns 0 for non-numeric input. That makes the exploit two steps: write a dummy entry to pass the guard, then send a non-numeric string to land on the entry 0 flag branch.First use the write option to create any entry (the content does not matter; it just makes num_entries >= 1 so the read path runs). Then use the read option and, instead of a number, type a non-numeric string such as 'a'. strtol turns that into 0, the entry_number == 0 branch fires, and the flag is printed.bashnc saturn.picoctf.net <PORT_FROM_INSTANCE>bash# 1) Option 1 (write): add any entry (e.g. data 'x', length 1)bash# 2) Option 2 (read): when asked for the entry number, type a non-numeric string:bashaExpected output
picoCTF{M4K3_5UR3_70_CH3CK_Y0UR_1NPU75_...}What didn't work first
Tried: Type '0' as the entry number during the read step instead of a non-numeric string.
Entering '0' does trigger the flag branch, since strtol legitimately returns 0 and the entry-number check fires. But the program may first validate that entry 0 does not exist in the array, and the exact control flow depends on the source. Reading it first shows which path is guarded and which is the real way in.
Tried: Skip the dummy write step and go straight to the read option with a non-numeric string.
With nothing stored, the entry count is 0 and the read handler errors out before it ever calls strtol on your input. The trick is unreachable until at least one entry exists, so the server just reports no entries and stops.
Learn more
The dummy write is the part that is easy to miss: with zero entries the read handler returns early before it ever evaluates the entry number, so the strtol trick alone does nothing. Once one entry exists, reading with non-numeric input lands on entry 0 and prints the flag.
The proper fix is to validate that the input is actually numeric (check
strtol'sendptr) and to bounds-check the index, rather than letting a parse failure silently select a privileged slot.Step 3Collect the flag
ObservationOnce the entry 0 branch runs, the server prints the flag straight to stdout. The exploit worked, and the flag only needs copying out of the terminal.Once the entry_number == 0 branch runs, the server prints the flag.Learn more
Static analyzers and careful review catch the unchecked
strtolreturn; fuzzers surface it by feeding non-numeric and boundary inputs (empty string, "a", "0", very large numbers).
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{M4K3_5UR3_70_CH3CK_Y0UR_1NPU75_...}
Not an integer underflow. strtol returns 0 for non-numeric input, and there is a flag branch for entry_number == 0, but the read path only runs once an entry exists. Create one dummy entry, then read with a non-numeric value (e.g. 'a') to trip the entry-0 branch and print the flag.