Description
The binary decodes the flag but it takes too long. Speed it up to get the flag.
Setup
Download the binary and make it executable.
wget <url>/need-for-speedchmod +x need-for-speedSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Run the binary and observe the delayObservationI noticed the challenge description said the binary 'takes too long,' which suggested running it first to observe exactly how and when it stalls so I could identify the mechanism causing the delay.Execute the binary. It will print something like 'Calculating key...' and then hang for a very long time (or use alarm() to kill itself after a few seconds). The actual flag decoding logic is correct - only the artificial delay prevents you from seeing the output.bash./need-for-speedWhat didn't work first
Tried: Running the binary with 'time ./need-for-speed' hoping it would complete if left long enough
The alarm(1) call delivers SIGALRM after exactly 1 second, which kills the process with a signal - not a timeout you can wait out. The busy-wait loop would take millions of seconds, so no amount of patience resolves this at the OS level.
Tried: Using 'strace ./need-for-speed' to trace system calls and spot the delay
strace shows the alarm() syscall and its argument, confirming the timer is there, but strace itself cannot suppress signal delivery. The process still dies from SIGALRM after 1 second; you need a debugger that can handle signals, not just observe them.
Learn more
Binaries can use
sleep(),alarm(), or busy-wait loops to introduce artificial delays.alarm(n)sends SIGALRM to the process after n seconds, killing it by default. These are used in CTF challenges as anti-automation measures.Step 2
Use GDB to bypass the SIGALRM timer and get the flagObservationI noticed from running the binary (and from strace output) that an alarm() syscall was scheduling a SIGALRM to kill the process after 1 second, which suggested using GDB's signal-handling capability to suppress SIGALRM delivery so the slow calculate_key loop could run to completion.Open the binary in GDB. Before running, tell GDB to ignore SIGALRM so the alarm handler never fires. Then simply run the program - the slow calculate_key loop (which counts from 0xec61038e down to 0xd8c2071c and returns 0xd8c2071c as the key) now has unlimited time to complete, and print_flag() prints the flag. An alternate approach is to break at main, call get_key() and print_flag() directly without running the full program flow.bashgdb ./need-for-speedbashhandle SIGALRM ignorebashrunbash# Alternate: call functions directly insteadbash# break mainbash# runbash# call (int) get_key()bash# call (int) print_flag()Expected output
picoCTF{Good job keeping bus #b3a1d39c speeding along!}What didn't work first
Tried: Using 'handle SIGALRM stop' instead of 'handle SIGALRM ignore' in GDB
'handle SIGALRM stop' tells GDB to pause execution when SIGALRM arrives and then pass the signal to the program. The alarm still fires, the process still receives and dies from SIGALRM - you just get a GDB prompt at the moment of death. 'ignore' is required so GDB discards the signal entirely and never delivers it.
Tried: Setting a breakpoint on print_flag and running without handling SIGALRM first
GDB still forwards unhandled signals to the inferior by default. The alarm fires at 1 second, SIGALRM kills the process before the calculate_key loop finishes, and the breakpoint on print_flag is never reached. The signal must be suppressed with 'handle SIGALRM ignore' before issuing 'run'.
Learn more
The binary calls
set_timer()which invokesalarm(1)- this schedules a SIGALRM to fire after 1 second, killing the program. Thenget_key()callscalculate_key(), which runs a countdown loop from0xec61038edown to0xd8c2071c- far too slow to finish in 1 second. The loop returns0xd8c2071cas the key used to decrypt the flag.handle SIGALRM ignoretells GDB to not stop, not print, and not pass SIGALRM to the program. With the timer defanged, the calculation loop runs to completion andprint_flag()is reached normally. Callingcall (int) get_key()thencall (int) print_flag()is an equivalent shortcut that skips even the timer setup.Step 3
Read the flagObservationI noticed GDB completed the run without terminating early after suppressing SIGALRM, which meant the print_flag() call had executed and the decrypted flag should appear in the terminal output.After bypassing the delay, the binary completes its flag decoding and prints it. Copy the output.Learn more
Patching a binary with
xxd+ a hex editor is another approach: find thecall alarminstruction bytes and overwrite them with NOP instructions (0x90). This permanently removes the delay from the binary file.
Interactive tools
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
Flag
Reveal flag
picoCTF{Good job keeping bus #b3a1d39c speeding along!}
The binary kills itself with a SIGALRM timer before the (correct) decode finishes. Bypass the timer rather than racing it: in GDB run `handle SIGALRM ignore` (or set the key directly and call print_flag), then let it complete. The bus-ID hex in the flag is instance-specific.