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
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.
Step 1
Understand the HAProxy setupObservationI noticed the challenge provides both a HAProxy configuration file and application code, which suggested that the flag's exposure depends on understanding how the two interact rather than finding a hidden route on the primary server.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-200Expected output
picoCTF{f41lur3_f41lur3_...}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 server never returns the flag regardless of the URL path - it only returns 'No flag in this service'. The flag lives exclusively on the backup server, which HAProxy only routes to after the primary signals failure. No amount of path enumeration on the primary will expose 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 2
Flood the primary server to trigger failoverObservationI noticed from haproxy.cfg that HAProxy routes to the backup only when the primary returns a non-200 response, and the app code showed the primary enforces a 300 req/min rate limit with a 503 on excess, which suggested deliberately saturating that limit with parallel requests would force the failover to the backup holding the flag.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 -n1 -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>/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 shell loop can only fire one curl at a time, so 500 requests may take several minutes - far too slow to exceed 300 requests per minute. The rate limit window resets and the primary never trips. The xargs -P50 flag runs 50 simultaneous curl processes, compressing 500 requests into a few seconds and reliably saturating 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 triggers a 503 from the primary, but HAProxy does not instantly failover on the first 503. It waits for its health-check interval (typically every few seconds) before marking the primary down and routing to the backup. Skipping the sleep 2 command means the final curl still hits the primary, which may have already recovered, returning 'No flag in this service' again.
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.
- JWT DecoderDecode JSON Web Tokens and inspect the header, payload, and signature. Useful for web exploitation challenges.
Flag
Reveal flag
picoCTF{f41lur3_f41lur3_...}
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.