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.
Setup
Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Understand the HAProxy setup
ObservationThe 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.bashcat haproxy.cfgbash# Primary server: rate-limits to 300 req/min, returns 503 on excessbash# Backup server: returns the flagbash# HAProxy: switches to backup when primary returns non-200What 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.
Step 2Flood the primary server to trigger failover
Observationhaproxy.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)bashseq 1 500 | xargs -P50 -I{} curl -s http://<HOST>:<PORT_FROM_INSTANCE>/ -o /dev/nullbash# Sleep briefly to let HAProxy detect the failurebashsleep 2bash# Now make one more request - it should hit the backup server with the flagbashcurl 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 -P50runs 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.