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.