│ Category: General Skills
│ Difficulty: Medium
│ Points: 200
│ Author: Imattas aka Zemi
────────────────────────────────────────────────────────────────────────────────
--[ Challenge Description ]--
│ Welcome to Failure Failure -- a high-available system. This challenge
simulates a real-world failover scenario.
────────────────────────────────────────────────────────────────────────────────
--[ Recon / Initial Analysis ]--
This is a General Skills challenge that simulates a high-availability / failover
environment. The name "Failure Failure" is a play on the concept of a "failure
of a failure" -- when the primary system fails AND the failover mechanism also
fails (or is intentionally broken), revealing something interesting.
In real-world infrastructure, high-availability (HA) systems use techniques
like:
- Active/Passive failover: A standby service takes over when the primary fails
- Service monitoring: Tools like systemctl, supervisord, or watchdog scripts
detect failures
- Redundant storage: Data is replicated across multiple locations
The challenge likely provides SSH access to a server running multiple services.
The goal is to explore the failover configuration, understand how the system
handles failures, and find the flag hidden somewhere in the failover mechanism.
:: Key Areas to Investigate
1. Running services: Check systemctl list-units or service --status-all for
active/inactive services
2. Service configurations: Examine /etc/systemd/system/ for custom service files
3. Failover scripts: Look for cron jobs, watchdog scripts, or systemd timers
that handle failover
4. Log files: Check /var/log/, journalctl, and application-specific logs for
clues
5. Backup/redundant files: Look for backup configurations, replicated data
stores, or secondary services
6. Environment variables: Failover configs sometimes store secrets in env vars
7. Process inspection: Use ps aux to see what is actually running vs what should
be running
────────────────────────────────────────────────────────────────────────────────
--[ Vulnerability / Observation ]--
The flag is hidden somewhere within the failover machinery rather than in plain
sight. Common patterns in this challenge type:
- The flag may be split across the primary and failover service configurations
- Stopping or crashing the primary service triggers the failover, which reveals
the flag
- The failover configuration file itself contains the flag as a "secret" or
"token"
- Logs from a previous failover event contain the flag
- A backup/replica database or file store contains the flag
The core insight is that triggering the failover (by stopping/killing the
primary) causes the standby mechanism to expose the flag — or it is already
sitting in a config/log/env that thorough enumeration will surface.
────────────────────────────────────────────────────────────────────────────────
--[ Exploitation / Solution ]--
:: Step 1: Connect and Enumerate
-- bash --
ssh user@challenge-server -p PORT
Once connected, enumerate the system:
-- bash --
# List all services
systemctl list-units --type=service --all
# Check for custom services
ls -la /etc/systemd/system/
# Look for failover-related scripts
find / -name "*failover*" -o -name "*backup*" -o -name "*replica*" 2>/dev/null
# Check cron jobs
crontab -l
cat /etc/crontab
ls -la /etc/cron.d/
:: Step 2: Examine Failover Configuration
-- bash --
# Read service unit files
cat /etc/systemd/system/primary.service
cat /etc/systemd/system/failover.service
# Check environment files referenced by services
cat /etc/default/primary
cat /etc/sysconfig/failover
# Look at ExecStartPre, ExecStart, ExecStop, ExecStartPost directives
# The flag may be in one of the scripts these reference
:: Step 3: Trigger the Failover
-- bash --
# Stop the primary service to trigger failover
sudo systemctl stop primary.service
# Or simulate a failure
kill -9 $(pgrep primary-app)
# Watch what happens
journalctl -f
systemctl status failover.service
:: Step 4: Inspect the Result
After triggering failover, check:
-- bash --
# New service output
curl localhost:PORT
cat /tmp/failover-output.txt
# Logs
journalctl -u failover.service
journalctl -u primary.service
# Any new files created
find /tmp -newer /etc/hostname
:: Step 5: Look in Common Hidden Locations
-- bash --
# Check all config files for flag patterns
grep -r "picoCTF" /etc/ 2>/dev/null
grep -r "picoCTF" /var/ 2>/dev/null
grep -r "picoCTF" /opt/ 2>/dev/null
grep -r "picoCTF" /home/ 2>/dev/null
# Check environment of running processes
cat /proc/*/environ 2>/dev/null | tr '\0' '\n' | grep -i flag
# Check systemd unit overrides
systemctl cat primary.service
systemctl cat failover.service
The flag will be revealed through one of these enumeration steps -- either
directly in a configuration file, in the output after triggering failover, or in
the logs of the failover event.
────────────────────────────────────────────────────────────────────────────────
--[ Full Exploit Script ]--
-- python --
#!/usr/bin/env python3
"""
Failure Failure - picoCTF 2026
Category: General Skills | Points: 200
A high-availability failover scenario challenge.
This script automates the enumeration and flag extraction process
by connecting via SSH and exploring the failover configuration.
Usage:
python3 solve.py REMOTE_HOST PORT USERNAME PASSWORD
python3 solve.py REMOTE_HOST PORT USERNAME --keyfile=~/.ssh/id_rsa
Example:
python3 solve.py titan.picoctf.net 12345 ctf-player password123
Requirements:
pip install paramiko pwntools
"""
import sys
import re
import time
try:
import paramiko
except ImportError:
print("Install paramiko: pip install paramiko")
sys.exit(1)
# ============================================================
# CONFIGURATION
# ============================================================
# Commands to run for enumeration (in order)
ENUM_COMMANDS = [
# --- Phase 1: Basic enumeration ---
"echo '=== WHOAMI ===' && whoami",
"echo '=== HOSTNAME ===' && hostname",
"echo '=== HOME DIR ===' && ls -la ~",
# --- Phase 2: Service enumeration ---
"echo '=== SERVICES ===' && systemctl list-units --type=service --all 2>/dev/null || service --status-all 2>/dev/null",
"echo '=== CUSTOM SYSTEMD ===' && ls -la /etc/systemd/system/ 2>/dev/null",
"echo '=== TIMERS ===' && systemctl list-timers --all 2>/dev/null",
# --- Phase 3: Search for failover configs ---
"echo '=== FAILOVER FILES ===' && find / -maxdepth 4 \\( -name '*failover*' -o -name '*backup*' -o -name '*replica*' -o -name '*standby*' -o -name '*secondary*' -o -name '*ha-*' -o -name '*primary*' \\) 2>/dev/null",
# --- Phase 4: Search for flag directly ---
"echo '=== FLAG SEARCH ===' && grep -rl 'picoCTF' /etc/ /var/ /opt/ /home/ /tmp/ /root/ 2>/dev/null",
"echo '=== FLAG IN ENV ===' && env | grep -i flag 2>/dev/null; cat /proc/*/environ 2>/dev/null | tr '\\0' '\\n' | grep -i flag 2>/dev/null",
# --- Phase 5: Check cron and scheduled tasks ---
"echo '=== CRON ===' && crontab -l 2>/dev/null; cat /etc/crontab 2>/dev/null; ls -la /etc/cron.d/ 2>/dev/null",
# --- Phase 6: Log inspection ---
"echo '=== JOURNAL RECENT ===' && journalctl --no-pager -n 50 2>/dev/null",
"echo '=== SYSLOG ===' && tail -50 /var/log/syslog 2>/dev/null; tail -50 /var/log/messages 2>/dev/null",
# --- Phase 7: Process inspection ---
"echo '=== PROCESSES ===' && ps aux 2>/dev/null",
# --- Phase 8: Network services ---
"echo '=== LISTENING PORTS ===' && ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null",
# --- Phase 9: Read service configs ---
"echo '=== SERVICE CONFIGS ===' && for f in /etc/systemd/system/*.service; do echo \"--- $f ---\"; cat \"$f\" 2>/dev/null; done",
# --- Phase 10: Check /opt and application dirs ---
"echo '=== OPT DIR ===' && ls -laR /opt/ 2>/dev/null",
"echo '=== TMP DIR ===' && ls -la /tmp/ 2>/dev/null",
# --- Phase 11: Check sudo privileges ---
"echo '=== SUDO PRIVS ===' && sudo -l 2>/dev/null",
]
# Commands to trigger failover (run after enumeration if flag not found)
FAILOVER_COMMANDS = [
# Try stopping services that look like primary/main services
"echo '=== TRIGGERING FAILOVER ===' && for svc in $(systemctl list-units --type=service --state=running --plain --no-legend 2>/dev/null | awk '{print $1}'); do echo \"Service: $svc\"; done",
# Attempt to stop primary-looking services (safe approach: just restart)
"sudo systemctl stop primary.service 2>/dev/null; sudo systemctl stop main.service 2>/dev/null; sudo systemctl stop app.service 2>/dev/null",
# Check what happened
"echo '=== POST-FAILOVER STATUS ===' && systemctl list-units --type=service --all 2>/dev/null",
"echo '=== POST-FAILOVER LOGS ===' && journalctl --no-pager -n 30 2>/dev/null",
"echo '=== POST-FAILOVER FLAG ===' && grep -rl 'picoCTF' /etc/ /var/ /opt/ /home/ /tmp/ 2>/dev/null",
]
# ============================================================
# SSH CONNECTION AND EXECUTION
# ============================================================
def ssh_connect(host, port, username, password=None, keyfile=None):
"""Establish SSH connection."""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connect_kwargs = {
"hostname": host,
"port": port,
"username": username,
}
if keyfile:
connect_kwargs["key_filename"] = keyfile
elif password:
connect_kwargs["password"] = password
print(f"[*] Connecting to {host}:{port} as {username}...")
client.connect(**connect_kwargs)
print("[+] Connected!")
return client
def run_command(client, cmd, timeout=10):
"""Execute a command over SSH and return output."""
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode(errors="ignore")
err = stderr.read().decode(errors="ignore")
return out + err
def search_for_flag(text):
"""Search for picoCTF flag pattern in text."""
matches = re.findall(r'picoCTF\{[^}]+\}', text)
return matches
# ============================================================
# MAIN SOLVE LOGIC
# ============================================================
def solve(host, port, username, password=None, keyfile=None):
client = ssh_connect(host, port, username, password, keyfile)
all_output = ""
flags_found = set()
# Phase 1: Enumeration
print("\n[*] Phase 1: Enumeration")
print("=" * 60)
for cmd in ENUM_COMMANDS:
print(f"\n[>] {cmd[:80]}...")
output = run_command(client, cmd)
all_output += output
if output.strip():
print(output[:500]) # Print first 500 chars
# Check for flags in output
flags = search_for_flag(output)
if flags:
for f in flags:
flags_found.add(f)
print(f"\n{'!' * 60}")
print(f"[!!!] FLAG FOUND: {f}")
print(f"{'!' * 60}")
if flags_found:
print(f"\n[+] Found {len(flags_found)} flag(s):")
for f in flags_found:
print(f" {f}")
client.close()
return flags_found
# Phase 2: Trigger failover
print("\n\n[*] Phase 2: Triggering failover")
print("=" * 60)
for cmd in FAILOVER_COMMANDS:
print(f"\n[>] {cmd[:80]}...")
output = run_command(client, cmd)
all_output += output
if output.strip():
print(output[:500])
flags = search_for_flag(output)
if flags:
for f in flags:
flags_found.add(f)
print(f"\n{'!' * 60}")
print(f"[!!!] FLAG FOUND: {f}")
print(f"{'!' * 60}")
# Phase 3: Post-failover deep scan
print("\n\n[*] Phase 3: Post-failover deep scan")
print("=" * 60)
time.sleep(2) # Wait for failover to complete
deep_scan = [
"grep -rl 'picoCTF' / 2>/dev/null | head -20",
"find / -maxdepth 3 -name 'flag*' -o -name '*.flag' 2>/dev/null",
"journalctl --no-pager 2>/dev/null | grep -i 'picoCTF\\|flag'",
]
for cmd in deep_scan:
print(f"\n[>] {cmd[:80]}...")
output = run_command(client, cmd, timeout=15)
all_output += output
if output.strip():
print(output[:500])
# If we find files containing the flag, read them
for line in output.strip().split('\n'):
line = line.strip()
if line and not line.startswith('[') and '/' in line:
file_output = run_command(client, f"cat '{line}' 2>/dev/null")
flags = search_for_flag(file_output)
if flags:
for f in flags:
flags_found.add(f)
print(f"\n[!!!] FLAG FOUND in {line}: {f}")
# Final report
print("\n" + "=" * 60)
if flags_found:
print(f"[+] Total flags found: {len(flags_found)}")
for f in flags_found:
print(f" FLAG: {f}")
else:
print("[-] No flags found automatically.")
print(" Manual investigation may be needed.")
print(" Review the enumeration output above for clues.")
print("\n Common things to try manually:")
print(" - Read service unit files and referenced scripts")
print(" - Stop/restart specific services and check logs")
print(" - Look for hidden files: find / -name '.*' 2>/dev/null")
print(" - Check database files: find / -name '*.db' -o -name '*.sqlite' 2>/dev/null")
client.close()
return flags_found
# ============================================================
# ENTRY POINT
# ============================================================
if __name__ == "__main__":
if len(sys.argv) < 4:
print(f"Usage: {sys.argv[0]} HOST PORT USERNAME [PASSWORD]")
print(f" {sys.argv[0]} HOST PORT USERNAME --keyfile=PATH")
print()
print(f"Example: {sys.argv[0]} titan.picoctf.net 12345 ctf-player password")
sys.exit(1)
host = sys.argv[1]
port = int(sys.argv[2])
username = sys.argv[3]
password = None
keyfile = None
if len(sys.argv) >= 5:
arg = sys.argv[4]
if arg.startswith("--keyfile="):
keyfile = arg.split("=", 1)[1]
else:
password = arg
solve(host, port, username, password=password, keyfile=keyfile)
────────────────────────────────────────────────────────────────────────────────
--[ Key Takeaways ]--
- General Skills "scenario" challenges reward methodical Linux enumeration:
services, unit files, timers, cron, logs, environment, processes, and listening
ports.
- High-availability setups hide secrets in failover machinery — service configs
(ExecStartPre/ExecStart/ExecStop), referenced scripts, or logs of past failover
events.
- Triggering the failover itself (stopping/killing the primary service) can be
the action that surfaces the flag.
- A broad grep -r "picoCTF" across /etc, /var, /opt, /home, /tmp, plus
/proc/*/environ, catches flags stashed in config files or process environments.
- paramiko lets you script the entire SSH enumeration-and-trigger workflow end
to end.