Skip to main content

Failure Failure picoCTF 2026 Solution

A load-balanced web service behaves differently depending on how requests are routed. Find the right path.

Published: March 20, 2026Updated: September 20, 2026

Description

Welcome to Failure Failure - a high-availability system challenge. It simulates a real-world failover scenario where one server is prioritized over the other. A load balancer stands between you and the flag - it won't hand over the flag until you force its hand.

Launch the challenge instance and note the URL.
Download the HAProxy configuration and application code provided with the challenge.
Visiting the URL returns 'No flag in this service' from the primary server.

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Understand the HAProxy setup
    Observation
    The challenge gives you both a HAProxy config and the application code, so the flag depends on how those two interact rather than on a hidden route somewhere.
    Read the HAProxy configuration and application code. The primary server rate-limits at 300 requests per minute and returns 503 when exceeded. When the proxy sees a non-200 response it fails over to the backup server which returns the flag.
    bash
    cat haproxy.cfg
    bash
    # Primary server: rate-limits to 300 req/min, returns 503 on excess
    bash
    # Backup server: returns the flag
    bash
    # HAProxy: switches to backup when primary returns non-200
    What didn't work first

    Tried: Trying to extract the flag directly by visiting the /flag endpoint or guessing hidden routes on the primary server

    The primary never returns the flag whatever path you ask for; it only says there is no flag in this service. The flag lives on the backup, which HAProxy reaches only after the primary signals failure. Path enumeration will not find it.

    Tried: Reading only the app code and ignoring haproxy.cfg, assuming the flag is hidden in an environment variable or commented out in the source

    The app code confirms the primary returns no flag, but the failover logic that exposes the backup is entirely in haproxy.cfg. Skipping the config file means missing the 503-triggers-failover rule, which is the entire vulnerability. Both files together reveal the attack path.

    Learn more

    HAProxy is a high-performance TCP/HTTP load balancer. In this challenge it is configured in active-passive mode: all traffic goes to the primary backend. If the primary returns a non-200 response (such as a 503 Service Unavailable), HAProxy automatically routes subsequent requests to the backup server.

    The primary server enforces a rate limit of 300 requests per minute. Exceeding this causes the primary to return 503, which triggers HAProxy's failover to the backup. The backup server does not have this rate limit and serves the flag directly.

  2. Step 2Flood the primary server to trigger failover
    Observation
    haproxy.cfg routes to the backup only when the primary answers with something other than 200, and the app enforces a 300-per-minute rate limit that returns 503 past it. Saturate that limit with parallel requests and the failover takes you to the backup.
    Send more than 300 requests to the endpoint in under a minute to trigger the rate limit. Use xargs with parallel workers to send the requests fast enough.
    bash
    # Generate 500 requests in parallel using xargs (50 threads)
    bash
    seq 1 500 | xargs -P50 -I{} curl -s http://<HOST>:<PORT_FROM_INSTANCE>/ -o /dev/null
    bash
    # Sleep briefly to let HAProxy detect the failure
    bash
    sleep 2
    bash
    # Now make one more request - it should hit the backup server with the flag
    bash
    curl http://<HOST>:<PORT_FROM_INSTANCE>/

    Expected output

    picoCTF{f4il0v3r_f0r_7h3_w1n_...}
    What didn't work first

    Tried: Sending 500 requests sequentially with a loop (for i in $(seq 1 500); do curl ...; done) instead of using xargs parallel workers

    A sequential loop fires one request at a time, so 500 of them take minutes and never exceed 300 in any one minute. The window resets and the primary stays healthy. Running 50 in parallel compresses those 500 into a few seconds and saturates the limit.

    Tried: Making exactly 300 or 301 requests and then immediately checking the response, expecting the flag on the 301st request

    The rate limit produces a 503, but HAProxy does not fail over on the first one. It waits for its health-check interval, a few seconds, before marking the primary down. Skip the pause and your final request still lands on the primary, which may have recovered by then.

    Learn more

    xargs -P50 runs 50 curl processes in parallel. With 500 requests spread across 50 threads, the primary receives well over 300 requests per minute and triggers its rate limiter, returning 503. HAProxy detects this failure and marks the primary as down.

    HAProxy polls backend health at an interval (typically every few seconds). After the primary starts returning 503 errors, HAProxy switches traffic to the backup on the next health check cycle. The sleep gives HAProxy time to detect the failure before you make the final request.

    In real-world attacks, this technique (deliberately overwhelming a primary to force failover to a less-hardened backup) is a form of availability attack. Proper HA configurations apply rate limiting at the load-balancer level (not just the application), use circuit breakers that degrade gracefully, and apply the same security controls to backup servers as to primary servers.

Interactive tools
  • URL Encoder / DecoderEncode and decode URL-encoded (percent-encoded) strings. Useful for web exploitation challenges involving query parameters, form data, and HTTP headers.

Flag

Reveal flag

picoCTF{f4il0v3r_f0r_7h3_w1n_...}

Flood the primary server with over 300 requests/minute using parallel curl via xargs to trigger its rate limit (503 response). HAProxy fails over to the backup server which returns the flag.

Key takeaway

A load balancer routes traffic away from unhealthy backends automatically, and that logic becomes a security primitive the moment the backup is less hardened than the primary. Deliberately tripping the primary's failure condition to redirect traffic is an availability attack, and it shows up in real engagements wherever rate limits, circuit breakers, or error thresholds sit at the application layer rather than the balancer. The same holds for CDN origin fallbacks, DNS failover, and read-replica promotion.

Related reading

Useful tools for General Skills

Where to go next