Files
attack-defense-platform/panel/verify_cred_users.py
T

83 lines
3.0 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Verify /api/credential reports the SSH user the container ACTUALLY has.
The panel proxies to the global receiver, which only knows the 6 native
GEMASTIK XVIII challenges -- the 10 imported XVI/XVII ones 500 there. For those
the UI must read the credential from the TEAM's own receiver (which is the
one that actually runs the checkers), not from :18080.
This test reports, per team, the username /credential would show vs the
`ssh_user` the registry (and chpasswd) uses.
"""
import json
import os
import subprocess
import sys
import urllib.request
import urllib.error
import base64
from pathlib import Path
BASE = Path("/opt/gemastik18-final")
ENV = BASE / "panel/.env"
cfg = {}
for line in ENV.read_text().splitlines():
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
cfg[k.strip()] = v.strip()
PANEL = "http://127.0.0.1:18081"
def post(path, payload, cookie=None):
data = json.dumps(payload).encode()
req = urllib.request.Request(PANEL + path, data=data, method="POST",
headers={"Content-Type": "application/json"})
if cookie:
req.add_header("Cookie", cookie)
with urllib.request.urlopen(req, timeout=30) as r:
return r.read().decode(), r.headers.get("Set-Cookie", "")
body, setc = post("/api/login", {"user": cfg.get("PANEL_ADMIN_USER", "admin"),
"pass": cfg.get("PANEL_ADMIN_PASS", "")})
cookie = "; ".join(s.split(";")[0] for s in setc.split(",") if "=" in s)
print("login:", body)
def get(path):
req = urllib.request.Request(PANEL + path, headers={"Cookie": cookie})
try:
with urllib.request.urlopen(req, timeout=60) as r:
return r.read().decode()
except urllib.error.HTTPError as e:
return f"HTTP {e.code}"
reg = json.loads((BASE / "teams/challenge_registry.json").read_text())
ssh_users = {c["name"]: c.get("ssh_user", "ctfuser") for c in reg.get("challenges", [])}
teams = json.loads(get("/api/teams"))["teams"]
ok = bad = 0
for t in teams:
idx = t["index"]
st = json.loads((BASE / f"teams/team{idx}/state.json").read_text())
names = list(st["ports"].keys())
names = [n for n in names if n not in ("receiver", "panel")]
print(f"\n=== team{idx} ({t.get('label')}) — {len(names)} challenges ===")
for n in sorted(names):
raw = get(f"/api/credential/{n}?team={idx}")
try:
d = json.loads(raw)
except Exception:
d = {"error": raw[:40]}
want = ssh_users.get(n, "?")
got = d.get("username")
haspw = bool(d.get("password"))
if got == want and haspw:
print(f" {n:<15} {got:<8} pw={'yes' if haspw else 'NO '} OK")
ok += 1
else:
note = "" if got else f" <- {d.get('error', raw)[:30]}"
print(f" {n:<15} {str(got):<8} want={want:<8} pw={'yes' if haspw else 'NO '}{note}")
bad += 1
print(f"\n{ok} correct, {bad} wrong/missing")
sys.exit(0)