Download Horsepower picoCTF 2021 Solution

Published: April 2, 2026

Description

Can you beat the timer? Exploit a time-sensitive vulnerability to get the flag.

Download the binary.

bash
wget https://mercury.picoctf.net/static/.../horsepower
bash
chmod +x horsepower
bash
checksec horsepower
  1. Step 1Identify the timing primitive
    Disassemble and look for calls to alarm, setitimer, or setrlimit. strace also reveals the timer call at runtime. The exact primitive determines whether you can race it, overwrite its GOT entry, or just let it fire and reconnect.
    bash
    ./horsepower
    bash
    objdump -d horsepower | grep -E 'call.*(alarm|setitimer|setrlimit)@plt'
    bash
    strace -e trace=alarm,setitimer,setrlimit ./horsepower 2>&1 | head -10
    Learn more

    Time-limited challenges use the alarm() system call to set a timer that sends SIGALRM after N seconds, terminating the program. This is a common CTF pattern to prevent brute-force attacks. The exploit must be fast enough to complete before the alarm fires, or must find a way to avoid the alarm.

    Strategies for time-limited exploits:

    • Scripted exploit: Automate with pwntools - scripted exploits run in milliseconds, easily beating a 5-60 second timer.
    • Disable alarm: If you can overwrite the alarm() GOT entry (via format string write) with a no-op function, the timer never fires.
    • Signal handler: In some scenarios, overwriting the SIGALRM handler to jump to a useful function instead of terminating.
  2. Step 2Find the unsafe input read in the disassembly
    Hunt the disassembly for unsafe input functions and trace where the result lands on the stack. The classic shape is a call to gets or read into a fixed-size buffer with no length check.
    bash
    # Look for the usual suspects directly:
    bash
    objdump -d horsepower | grep -E 'call.*(gets|read|scanf|sprintf)@plt'
    bash
    # Then disassemble the calling function and see the buffer size:
    bash
    objdump -d horsepower | sed -n '/<vuln>:/,/^$/p'
    Learn more

    What a vulnerable read looks like in objdump. A classic gets-into-stack-buffer call site looks like this:

    0000000000401196 <vuln>:
      401196: f3 0f 1e fa            endbr64
      40119a: 55                     push   rbp
      40119b: 48 89 e5               mov    rbp,rsp
      40119e: 48 83 ec 30            sub    rsp,0x30          ; 48 bytes of locals
      4011a2: 48 8d 45 d0            lea    rax,[rbp-0x30]    ; &buf
      4011a6: 48 89 c7               mov    rdi,rax           ; arg0 = &buf
      4011a9: e8 a2 fe ff ff         call   401050 <gets@plt> ; UNBOUNDED READ
      4011ae: 90                     nop
      4011af: c9                     leave
      4011b0: c3                     ret

    The buffer is 48 bytes (0x30) and gets has no length cap, so anything beyond 48 bytes overruns into the saved RBP and saved RIP - the standard ret2win shape.

    Race conditions are another form of time-sensitive vulnerability. If the program has a TOCTOU (time-of-check to time-of-use) race, checking a condition then acting on it non-atomically, another thread can change the state between the check and the use. For single-threaded programs, this requires signal handler races or filesystem-level races (e.g., symlink attacks on files checked with access() then opened with open()).

  3. Step 3Build and automate the exploit
    Write a pwntools script that fully automates the exploit within the time limit. Test locally to confirm it completes before the alarm fires.
    python
    python3 - <<'EOF'
    from pwn import *
    import time
    
    start = time.time()
    
    e = ELF('./horsepower')
    p = remote('mercury.picoctf.net', <PORT_FROM_INSTANCE>)
    
    # Move quickly - don't add unnecessary sleeps
    # ...
    
    log.info(f"Exploit ran in {time.time()-start:.2f}s")
    p.interactive()
    EOF
    Learn more

    pwntools' remote() connection and subsequent I/O operations are fast enough to exploit services with 30-60 second timers. For very tight timers (1-5 seconds), precompute all payloads before connecting so no computation happens after the connection is established, only the send and receive operations. For more on pwntools timing tricks and connect-and-go benchmarks, see the pwntools guide.

Flag

picoCTF{...}

Time-limited exploits are defeated by scripting with pwntools - automated exploits complete in milliseconds, easily within any reasonable timer window.

Want more picoCTF 2021 writeups?

Useful tools for Binary Exploitation

Related reading

What to try next