#!/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= 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)