draggable topology + attack visualizer + tools in containers
- topology nodes draggable (pointer events, SVG transform), layout hint shown - attack visualizer: /api/attacks logs attacker->target events; red pulsing dashed arcs on recent attacks (60s hot), ⚔ counts ok/fail - submit_flag now takes attacker_idx vs target_idx (A/D semantics); UI has target dropdown (enemy teams), leaderboard records target - containers get vim+curl+wget+netcat+git+pip3 (Dockerfiles blogpost/cdn/ phew/sheesh/warmup); warmup base ubuntu:20.04 EOL -> 24.04 - team portal: target dropdown refreshed after login (was empty pre-auth)
This commit is contained in:
+39
-9
@@ -374,9 +374,15 @@ def randomize_flags(idx: int) -> dict:
|
||||
return mapping
|
||||
|
||||
|
||||
def submit_flag(team_idx: int, chall: str, flag: str, team_name: str = "") -> dict:
|
||||
"""Validate a submitted flag against the owning team's challenge flag."""
|
||||
team_dir = TEAMS_DIR / f"team{team_idx}"
|
||||
def submit_flag(target_idx: int, chall: str, flag: str,
|
||||
attacker_idx: int = None, attacker_name: str = "") -> dict:
|
||||
"""Validate a submitted flag against the TARGET team's challenge flag.
|
||||
|
||||
A/D semantics: attacker_idx scores by stealing target_idx's flag.
|
||||
Back-compat: attacker_idx defaults to target_idx (self-submit).
|
||||
"""
|
||||
attacker_idx = attacker_idx if attacker_idx else target_idx
|
||||
team_dir = TEAMS_DIR / f"team{target_idx}"
|
||||
if not (team_dir / "state.json").exists():
|
||||
return {"success": False, "error": "unknown team"}
|
||||
flags_dir = team_dir / "receiver" / "flags"
|
||||
@@ -384,23 +390,47 @@ def submit_flag(team_idx: int, chall: str, flag: str, team_name: str = "") -> di
|
||||
if not expected:
|
||||
return {"success": False, "error": "challenge not found"}
|
||||
if flag.strip() != expected:
|
||||
_log_attack(attacker_idx, target_idx, chall, flag[:24], success=False)
|
||||
return {"success": False, "error": "wrong flag"}
|
||||
# record leaderboard entry
|
||||
_log_attack(attacker_idx, target_idx, chall, flag, success=True)
|
||||
# record leaderboard entry — score goes to the ATTACKER
|
||||
lb_path = TEAMS_DIR / "leaderboard.json"
|
||||
lb = json.loads(lb_path.read_text()) if lb_path.exists() else {"solves": []}
|
||||
entry = {
|
||||
"team": team_idx,
|
||||
"team_name": team_name or f"Team {team_idx}",
|
||||
"team": attacker_idx,
|
||||
"team_name": attacker_name or f"Team {attacker_idx}",
|
||||
"challenge": chall,
|
||||
"target": target_idx,
|
||||
"flag": flag,
|
||||
"ts": time.time(),
|
||||
"ts_human": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
# avoid double-solve duplicates (same flag + same team)
|
||||
if not any(e["team"] == team_idx and e["challenge"] == chall for e in lb["solves"]):
|
||||
# avoid double-solve duplicates (same flag + same attacker team)
|
||||
if not any(e["team"] == attacker_idx and e["challenge"] == chall
|
||||
and e.get("target") == target_idx for e in lb["solves"]):
|
||||
lb["solves"].append(entry)
|
||||
lb_path.write_text(json.dumps(lb, indent=2))
|
||||
return {"success": True, "team": team_idx, "challenge": chall}
|
||||
return {"success": True, "team": attacker_idx, "target": target_idx, "challenge": chall}
|
||||
|
||||
def _log_attack(attacker_team: int, target_team: int, target_chall: str, flag_hint: str, success: bool):
|
||||
"""Append a row to the attack log (used by the topology attack visualizer)."""
|
||||
try:
|
||||
ap = TEAMS_DIR / "attacks.json"
|
||||
log = json.loads(ap.read_text()) if ap.exists() else {"events": []}
|
||||
log["events"].append({
|
||||
"attacker": attacker_team,
|
||||
"target": target_team,
|
||||
"challenge": target_chall,
|
||||
"flag_hint": flag_hint,
|
||||
"success": success,
|
||||
"ts": time.time(),
|
||||
"ts_human": datetime.now().strftime("%H:%M:%S"),
|
||||
})
|
||||
# keep last 200
|
||||
log["events"] = log["events"][-200:]
|
||||
ap.write_text(json.dumps(log, indent=2))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def reset_scores() -> dict:
|
||||
"""Wipe the leaderboard (all solves removed)."""
|
||||
|
||||
Reference in New Issue
Block a user