Offensive Lab 04 — Weighted Attack-Path Solver

Lens: Lateral movement, weighted. WARMUP: Chapter 7. Extends: Lab 02.

The problem

Lab 02 found reachability and choke points over an unweighted graph. Real attackers take the path of least resistance, not fewest hops — a reused clear-text admin session is cheaper than cracking a Kerberos ticket. This lab weights each edge by attacker effort and finds the cheapest path (Dijkstra), and computes formal dominators — nodes on every path from the foothold to a target, i.e. the true choke points a defender must cut.

What you build (lab.py)

  • cheapest_path(edges, start, target)(path, cost) via Dijkstra; (None, inf) if unreachable.
  • path_cost(...) → the total attacker effort.
  • dominators(edges, start, target) → nodes whose removal disconnects the target (true choke points).

Cases the tests cover

  • The cheapest path takes the low-effort route even when it's longer in hops; a cheaper long path beats an expensive direct edge; unreachable targets return (None, inf).
  • Dominators: a target with two paths has no single dominator; a target reachable only via one node has that node as a dominator; a linear chain's interior nodes are all dominators.

Run

pip install -r requirements.txt
LAB_MODULE=solution pytest -q
pytest -q

Hardening / extensions

  • Derive edge cost from real signals (auth type, MFA, patch level, exploit availability/EPSS).
  • Compute a minimum-cost cut (cheapest set of edges to disconnect the crown jewels), not just dominator nodes.
  • Import a BloodHound/Azure graph (authorized environments) and rank remediations by cost-to-cut.

Interview / resume

"Built a weighted attack-path solver (Dijkstra cheapest path + formal dominators) showing that attackers follow least-resistance, not shortest, paths — and that dominator nodes are the highest-leverage cuts for a defender."

Limitations: static weights on a directed graph; dominators via node-removal (not the linear-time dominator-tree algorithm); no min-cut.