|
| 1 | +# attackgraph |
| 2 | + |
| 3 | +**Attack graph construction from network topology and vulnerability scan data, with MITRE ATT&CK mapping and risk scoring.** |
| 4 | + |
| 5 | +## Problem |
| 6 | + |
| 7 | +Security teams face an overwhelming volume of vulnerability scan results but lack visibility into how those vulnerabilities chain together to form actual attack paths. A single medium-severity CVE on a web server might be the first step in a multi-hop path that reaches your crown jewel database. Without attack path analysis, teams waste time on isolated findings while critical chains go unaddressed. |
| 8 | + |
| 9 | +**attackgraph** solves this by: |
| 10 | + |
| 11 | +- Building directed attack graphs from network topology + vulnerability scan data |
| 12 | +- Computing which attack paths are actually feasible (network reachability, service matching, attack vector constraints) |
| 13 | +- Mapping each step to MITRE ATT&CK techniques |
| 14 | +- Scoring assets and paths by risk (CVSS, path length, asset criticality, cumulative probability) |
| 15 | + |
| 16 | +## Installation |
| 17 | + |
| 18 | +```bash |
| 19 | +pip install attackgraph |
| 20 | +``` |
| 21 | + |
| 22 | +Or from source: |
| 23 | + |
| 24 | +```bash |
| 25 | +git clone https://github.com/cwccie/attackgraph.git |
| 26 | +cd attackgraph |
| 27 | +pip install -e . |
| 28 | +``` |
| 29 | + |
| 30 | +## Quick Start |
| 31 | + |
| 32 | +### Python API |
| 33 | + |
| 34 | +```python |
| 35 | +from attackgraph import Asset, Vulnerability, AttackGraphBuilder |
| 36 | +from attackgraph.scoring import RiskScorer |
| 37 | +from attackgraph.models import AttackVector |
| 38 | + |
| 39 | +# Define assets |
| 40 | +web = Asset( |
| 41 | + id="webserver", |
| 42 | + ip="10.0.1.10", |
| 43 | + hostname="web-01", |
| 44 | + services=["http/80", "http/443"], |
| 45 | + criticality=0.7, |
| 46 | + network_zone="dmz", |
| 47 | +) |
| 48 | +db = Asset( |
| 49 | + id="database", |
| 50 | + ip="10.0.2.30", |
| 51 | + hostname="db-01", |
| 52 | + services=["mysql/3306"], |
| 53 | + criticality=1.0, |
| 54 | + network_zone="internal", |
| 55 | +) |
| 56 | + |
| 57 | +# Define vulnerabilities |
| 58 | +web_vuln = Vulnerability( |
| 59 | + cve_id="CVE-2024-1001", |
| 60 | + cvss=9.8, |
| 61 | + affected_service="http/80", |
| 62 | + attack_vector=AttackVector.NETWORK, |
| 63 | + description="Remote code execution in HTTP server", |
| 64 | +) |
| 65 | +db_vuln = Vulnerability( |
| 66 | + cve_id="CVE-2024-1007", |
| 67 | + cvss=9.1, |
| 68 | + affected_service="mysql/3306", |
| 69 | + attack_vector=AttackVector.NETWORK, |
| 70 | + description="MySQL remote code execution", |
| 71 | +) |
| 72 | + |
| 73 | +web.vulns.append(web_vuln) |
| 74 | +db.vulns.append(db_vuln) |
| 75 | + |
| 76 | +# Build the attack graph |
| 77 | +builder = AttackGraphBuilder( |
| 78 | + topology={"webserver": ["database"]}, |
| 79 | + assets=[web, db], |
| 80 | +) |
| 81 | +graph = builder.build() |
| 82 | + |
| 83 | +print(f"Nodes: {graph.node_count}, Edges: {graph.edge_count}") |
| 84 | + |
| 85 | +# Score risks |
| 86 | +scorer = RiskScorer(graph) |
| 87 | +scores = scorer.score_assets() |
| 88 | +for asset_id, score in sorted(scores.items(), key=lambda x: x[1], reverse=True): |
| 89 | + print(f" {asset_id}: {score:.2f}") |
| 90 | + |
| 91 | +# Find attack paths to database |
| 92 | +paths = scorer.score_paths("database") |
| 93 | +for p in paths: |
| 94 | + print(f" Path: {' -> '.join(p['path'])} Score: {p['score']:.2f}") |
| 95 | +``` |
| 96 | + |
| 97 | +### CLI |
| 98 | + |
| 99 | +```bash |
| 100 | +# Build an attack graph from topology and vulnerability scan |
| 101 | +attackgraph build --topology topology.yaml --vulns scan.csv --output graph.json |
| 102 | + |
| 103 | +# Score assets in the graph |
| 104 | +attackgraph score graph.json |
| 105 | + |
| 106 | +# Find attack paths to a target |
| 107 | +attackgraph paths graph.json --target database |
| 108 | +``` |
| 109 | + |
| 110 | +### Topology File Format (YAML) |
| 111 | + |
| 112 | +```yaml |
| 113 | +topology: |
| 114 | + firewall: [webserver] |
| 115 | + webserver: [appserver, workstation] |
| 116 | + appserver: [database] |
| 117 | + |
| 118 | +assets: |
| 119 | + firewall: |
| 120 | + id: firewall |
| 121 | + ip: 10.0.0.1 |
| 122 | + services: [http/443, ssh/22] |
| 123 | + criticality: 0.9 |
| 124 | + network_zone: perimeter |
| 125 | + webserver: |
| 126 | + id: webserver |
| 127 | + ip: 10.0.1.10 |
| 128 | + services: [http/80, http/443] |
| 129 | + criticality: 0.7 |
| 130 | + network_zone: dmz |
| 131 | +``` |
| 132 | +
|
| 133 | +### Vulnerability CSV Format |
| 134 | +
|
| 135 | +```csv |
| 136 | +ip,cve_id,cvss,service,attack_vector,description |
| 137 | +10.0.1.10,CVE-2024-1001,9.8,http/80,network,Remote code execution |
| 138 | +10.0.2.20,CVE-2024-1004,9.0,smb/445,network,SMB buffer overflow |
| 139 | +``` |
| 140 | + |
| 141 | +Nessus CSV exports are also supported with `--format nessus`. |
| 142 | + |
| 143 | +## Scoring Model |
| 144 | + |
| 145 | +Risk scores combine multiple factors: |
| 146 | + |
| 147 | +- **CVSS severity** — weighted blend of max and average CVSS per asset |
| 148 | +- **Asset criticality** — user-defined weight (0.0–1.0) reflecting business importance |
| 149 | +- **Exposure** — in-degree in the attack graph (more inbound attack vectors = higher risk) |
| 150 | +- **Path probability** — cumulative exploitation probability along each path |
| 151 | +- **Path length** — shorter paths are more dangerous (fewer steps to compromise) |
| 152 | + |
| 153 | +All scores are normalized to a 0.0–10.0 scale. |
| 154 | + |
| 155 | +## MITRE ATT&CK Mapping |
| 156 | + |
| 157 | +Each attack step is automatically mapped to an ATT&CK technique using: |
| 158 | + |
| 159 | +1. **Keyword matching** on CVE descriptions (e.g., "SQL injection" → T1190, "privilege escalation" → T1068) |
| 160 | +2. **Service-based heuristics** (e.g., HTTP → T1190, SMB → T1210, VPN → T1133) |
| 161 | +3. **Default fallback** to T1210 (Exploitation of Remote Services) |
| 162 | + |
| 163 | +## Export Formats |
| 164 | + |
| 165 | +- **JSON** — full graph with metadata, suitable for further processing |
| 166 | +- **GraphML** — for visualization in Gephi, yEd, Cytoscape |
| 167 | + |
| 168 | +## Docker |
| 169 | + |
| 170 | +```bash |
| 171 | +docker build -t attackgraph . |
| 172 | +docker run attackgraph --help |
| 173 | +docker run -v $(pwd)/data:/data attackgraph build -t /data/topology.yaml -V /data/vulns.csv |
| 174 | +``` |
| 175 | + |
| 176 | +## Development |
| 177 | + |
| 178 | +```bash |
| 179 | +pip install -e . |
| 180 | +pip install pytest pytest-cov ruff |
| 181 | + |
| 182 | +# Run tests |
| 183 | +pytest -v --cov=attackgraph |
| 184 | + |
| 185 | +# Lint |
| 186 | +ruff check src/ tests/ |
| 187 | +``` |
| 188 | + |
| 189 | +## License |
| 190 | + |
| 191 | +MIT License. Copyright (c) 2026 Corey Wade. |
0 commit comments