Skip to main content

Need For Speed picoCTF 2019 Solution

Reverse engineer a binary with a deliberate timing delay and bypass it to reveal the flag.

Published: April 2, 2026Updated: August 13, 2026

Description

The binary decodes the flag but it takes too long. Speed it up to get the flag.

Download the binary and make it executable.

bash
wget <url>/need-for-speed
bash
chmod +x need-for-speed

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Run the binary and observe the delay
    Observation
    The description says the binary takes too long. Run it first and watch exactly how and when it stalls, so the mechanism causing the delay gets identified rather than guessed at.
    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-speed
    What didn't work first

    Tried: Running the binary with 'time ./need-for-speed' hoping it would complete if left long enough

    alarm(1) delivers SIGALRM after exactly one second and kills the process with a signal; it is not a timeout you can outwait. The busy-wait loop would take millions of seconds, so no amount of patience fixes 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.

  2. Step 2Use GDB to bypass the SIGALRM timer and get the flag
    Observation
    Running the binary, and strace, show an alarm() syscall scheduling a SIGALRM to kill the process after one second. GDB can suppress that signal, which lets the slow calculate_key loop 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.
    bash
    gdb ./need-for-speed
    bash
    handle SIGALRM ignore
    bash
    run
    bash
    # Alternate: call functions directly instead
    bash
    # break main
    bash
    # run
    bash
    # 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 when SIGALRM arrives and then pass it along. The alarm still fires and the process still dies from it; you just get a GDB prompt at the moment of death. 'ignore' is what makes GDB discard the signal and never deliver it.

    Tried: Setting a breakpoint on print_flag and running without handling SIGALRM first

    GDB forwards unhandled signals to the inferior by default. The alarm fires at one second, SIGALRM kills the process before calculate_key finishes, and the breakpoint on print_flag is never reached. Suppress it with 'handle SIGALRM ignore' before you issue 'run'.

    Learn more

    The binary calls set_timer() which invokes alarm(1) - this schedules a SIGALRM to fire after 1 second, killing the program. Then get_key() calls calculate_key(), which runs a countdown loop from 0xec61038e down to 0xd8c2071c - far too slow to finish in 1 second. The loop returns 0xd8c2071c as the key used to decrypt the flag.

    handle SIGALRM ignore tells GDB to not stop, not print, and not pass SIGALRM to the program. With the timer defanged, the calculation loop runs to completion and print_flag() is reached normally. Calling call (int) get_key() then call (int) print_flag() is an equivalent shortcut that skips even the timer setup.

  3. Step 3Read the flag
    Observation
    With SIGALRM suppressed, GDB finishes the run without an early exit. That means print_flag() executed, so the decrypted flag should be sitting 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 the call alarm instruction 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.

Key takeaway

Anti-debugging tricks like alarm timers, signal handlers, and artificial busy loops are surface obstructions; they do not change what the binary actually computes. A debugger can intercept and suppress signals before they reach the process, patch out branches in memory, or call internal functions directly, stepping right around the intended control flow. Real malware uses the same SIGALRM and ptrace-detection tricks to slow down sandbox analysis, and the same debugger techniques defeat them.

Related reading

Useful tools for Reverse Engineering

Where to go next