Files
felhom.eu/documentation/audits/dockerhub-bigsteps-2026-10-28/termix_xff.py
T

41 lines
2.7 KiB
Python

#!/usr/bin/env python3
"""termix_xff.py LABEL — D58 / GHSA-7j7m on the Termix installed on scratch 9202: does a forged X-Forwarded-For let one
address skip the login limit? 7 wrong logins, 7 different usernames, 7 different forged X-Forwarded-For values:
LAN: from DooPlex straight to 9202's traefik (the household's LAN path; traefik does NOT trust this peer);
TUNNEL: from a throwaway curl container on 9202's felhom-tunnel network at 172.16.253.2 — the address cloudflared has on
a real box, the ONE peer whose forwarded headers traefik keeps (the internet path). The header is what Cloudflare
delivers: the visitor's forged value, then the real visitor address Cloudflare APPENDS (one constant, 203.0.113.99).
Fixed = an HTTP 429 by the 6th attempt (the per-address limit, MAX_ATTEMPTS 5). Not fixed = no 429 in 7.
usage: SC=<dir with .ctlpw> TARGET=9202 python3 termix_xff.py LABEL (prints, and writes xff-LABEL.json in EV)"""
import json, os, secrets, sys
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
label, EV = sys.argv[1], os.environ["EV"]
os.makedirs(EV, exist_ok=True)
sub = "termix"
w.login()
res = {"label": label, "image": w.guest("docker inspect -f '{{.Config.Image}}' termix").strip()}
lan = []
for i in range(7):
rc, code, out = w.app_curl(sub, "/users/login", "-H", "Content-Type: application/json",
"-H", f"X-Forwarded-For: 203.0.113.{10 + i}",
data=json.dumps({"username": "nobody" + secrets.token_hex(3), "password": "wrong-" + secrets.token_hex(4)}),
method="POST")
lan.append(code)
res["lan_codes"] = lan
host = f"{sub}.{w.DOMAIN}"
gc = w.GATE.get(sub) or w.gate_cookie(sub) or "" # the household's gate cookie, as the LAN half sends it; never printed
lines = ["for x in 11 12 13 14 15 16 17; do",
" curl -sk -o /dev/null -w '%{http_code} ' --resolve " + host + ":443:172.16.253.3 \\",
" -H \"X-Forwarded-For: 198.51.100.$x, 203.0.113.99\" -H 'Content-Type: application/json' -H \"Cookie: $GC\" \\",
" --data \"{\\\"username\\\":\\\"tun$x$$\\\",\\\"password\\\":\\\"wrong-pass-$x\\\"}\" https://" + host + "/users/login",
"done"]
script = ("cat > /tmp/xff.sh <<'XEOF'\n" + "\n".join(lines) + "\nXEOF\n"
"docker run --rm --network felhom-tunnel --ip 172.16.253.2 -e GC -v /tmp/xff.sh:/xff.sh:ro curlimages/curl:8.11.1 sh /xff.sh; rm -f /tmp/xff.sh")
out = w.guest("export GC=" + json.dumps(gc) + "\n" + script, timeout=300)
res["tunnel_codes"] = out.split()
res["lan_fixed"] = "429" in lan[:6]
res["tunnel_fixed"] = "429" in res["tunnel_codes"][:6]
print(json.dumps(res))
json.dump(res, open(os.path.join(EV, f"xff-{label}.json"), "w"), indent=1)