Attack Trees
Learn to visualize attack paths using attack trees. Understand how to construct, analyze, and use attack trees for systematic threat modeling.
Attack trees are a visual methodology for analyzing how an attacker might achieve a goal. Invented by Bruce Schneier, they provide a structured way to think through all possible attack paths—helping you identify threats that might be missed with linear analysis.
Unlike STRIDE (which categorizes threats) or DREAD (which prioritizes them), attack trees show how attacks happen. The tree structure makes complex attack scenarios understandable and helps identify the weakest links in your defenses.
Anatomy of an Attack Tree
An attack tree consists of:
- Root node: The attacker's ultimate goal
- Child nodes: Sub-goals or steps needed to achieve the parent
- Leaf nodes: Atomic attack actions
- AND/OR gates: How sub-goals combine
Basic Structure
[Attacker Goal]
|
+-------------+-------------+
| |
[Sub-goal A] [Sub-goal B]
| |
+-----+-----+ +-----+-----+
| | | |
[Step 1] [Step 2] [Step 3] [Step 4]
- OR relationship (default): Attacker needs to complete ANY child to achieve parent
- AND relationship: Attacker must complete ALL children to achieve parent
Building Your First Attack Tree
Let's model a common scenario: compromising a web application's admin account.
Step 1: Define the Root Goal
Start with a clear, specific attacker objective:
Goal: Gain Admin Access to Web Application
Step 2: Identify High-Level Approaches
What are the main ways to achieve this goal?
[Gain Admin Access]
|
+-----------------+------------------+
| | |
[Steal Admin [Exploit App [Social Engineer
Credentials] Vulnerability] Admin User]
Step 3: Decompose Each Approach
Break down each high-level approach into specific attack vectors:
[Gain Admin Access]
|
+----------------------+----------------------+
| | |
[Steal Admin [Exploit App [Social Engineer
Credentials] Vulnerability] Admin User]
| | |
+----+----+ +----+----+ +----+----+
| | | | | |
[Phishing] [Keylogger] [SQLi] [Auth [Pretexting] [Baiting]
Bypass]
Step 4: Continue Until Atomic Actions
Expand until you reach actions that can't be meaningfully decomposed:
[Phishing]
|
+---+---+
| |
[Send [Clone
Email] Login Page]
| |
| +----+----+
| | |
| [Buy [Setup
| Domain] Hosting]
|
+--+---+
| |
[Craft [Buy Victim
Lure] Email List]
Attack Tree Notation
Text-Based Notation
For documentation and code reviews, use indented text:
Goal: Gain Admin Access
OR
|-- Steal Credentials
| OR
| |-- Phishing Attack
| | AND
| | |-- Create Fake Login Page
| | |-- Send Convincing Email
| | |-- Victim Enters Credentials
| |
| |-- Credential Stuffing
| | AND
| | |-- Obtain Leaked Password List
| | |-- Admin Reuses Password
| |
| |-- Keylogger
| AND
| |-- Install Malware on Admin Machine
| |-- Admin Logs In
|
|-- Exploit Vulnerability
| OR
| |-- SQL Injection
| | AND
| | |-- Find Injectable Parameter
| | |-- Extract Admin Password Hash
| | |-- Crack Hash
| |
| |-- Authentication Bypass
| OR
| |-- JWT Algorithm Confusion
| |-- Session Fixation
|
|-- Social Engineering
OR
|-- Call IT Helpdesk
| AND
| |-- Impersonate Employee
| |-- Request Password Reset
|
|-- Physical Access
AND
|-- Tailgate into Office
|-- Access Unlocked Workstation
YAML Notation for Tooling
attack_tree:
goal: "Gain Admin Access"
type: OR
children:
- name: "Steal Credentials"
type: OR
children:
- name: "Phishing Attack"
type: AND
cost: 500
skill: 3
children:
- name: "Create Fake Login Page"
leaf: true
cost: 100
- name: "Send Convincing Email"
leaf: true
cost: 50
- name: "Victim Enters Credentials"
leaf: true
probability: 0.15
- name: "Credential Stuffing"
type: AND
cost: 200
children:
- name: "Obtain Leaked Password List"
leaf: true
cost: 100
- name: "Admin Reuses Password"
leaf: true
probability: 0.3
- name: "Exploit Vulnerability"
type: OR
children:
- name: "SQL Injection"
type: AND
skill: 5
children:
- name: "Find Injectable Parameter"
leaf: true
time_hours: 4
- name: "Extract Admin Hash"
leaf: true
- name: "Crack Password Hash"
leaf: true
time_hours: 24
Annotating Attack Trees
Add attributes to nodes to enable quantitative analysis:
Common Attributes
| Attribute | Description | Example Values |
|---|---|---|
| Cost | Resources needed (money, time) | $100, 4 hours |
| Skill | Technical expertise required | 1-10 scale |
| Probability | Likelihood of success | 0.0 - 1.0 |
| Detectability | Chance of being caught | Low/Medium/High |
| Prerequisites | What must happen first | "Network access" |
Annotated Example
# Python representation for automated analysis
attack_tree = {
'goal': 'Gain Admin Access',
'type': 'OR',
'children': [
{
'name': 'Phishing',
'type': 'AND',
'cost_usd': 500,
'time_hours': 8,
'skill_level': 3,
'success_probability': 0.15,
'detection_probability': 0.4,
'children': [
{'name': 'Setup fake domain', 'leaf': True, 'cost_usd': 15},
{'name': 'Clone login page', 'leaf': True, 'time_hours': 2},
{'name': 'Craft email', 'leaf': True, 'time_hours': 1},
{'name': 'Victim clicks link', 'leaf': True, 'probability': 0.3}
]
},
{
'name': 'SQL Injection',
'type': 'AND',
'cost_usd': 0,
'time_hours': 12,
'skill_level': 6,
'success_probability': 0.8, # If vuln exists
'detection_probability': 0.6,
'prerequisites': ['Vulnerable endpoint exists'],
'children': [
{'name': 'Find injectable param', 'leaf': True},
{'name': 'Extract hash', 'leaf': True},
{'name': 'Crack hash', 'leaf': True, 'time_hours': 24}
]
},
{
'name': 'Insider Threat',
'type': 'AND',
'cost_usd': 10000, # Bribe
'skill_level': 1,
'success_probability': 0.9,
'detection_probability': 0.2,
'children': [
{'name': 'Identify disgruntled employee', 'leaf': True},
{'name': 'Make offer', 'leaf': True},
{'name': 'Employee provides access', 'leaf': True}
]
}
]
}
Analyzing Attack Trees
Finding the Cheapest Attack Path
For OR nodes, take the minimum cost child. For AND nodes, sum all children:
def calculate_min_cost(node):
"""Calculate minimum cost to achieve this node."""
if node.get('leaf'):
return node.get('cost_usd', 0)
child_costs = [calculate_min_cost(child) for child in node['children']]
if node['type'] == 'OR':
return min(child_costs)
else: # AND
return sum(child_costs)
# For our example:
# - Phishing: $500 (fixed campaign cost)
# - SQL Injection: $0 (just needs skill)
# - Insider: $10,000 (bribe)
# Minimum: SQL Injection at $0
Finding the Easiest Attack Path
Consider both skill and success probability:
def find_easiest_path(node, current_path=None):
"""Find attack path with lowest skill requirement and highest success."""
current_path = current_path or []
if node.get('leaf'):
return [node['name']], node.get('skill_level', 1)
if node['type'] == 'OR':
# Find child with lowest skill requirement
best_path = None
best_skill = float('inf')
for child in node['children']:
path, skill = find_easiest_path(child)
if skill < best_skill:
best_skill = skill
best_path = [child['name']] + path
return best_path, best_skill
else: # AND - must do all, take max skill
all_paths = []
max_skill = 0
for child in node['children']:
path, skill = find_easiest_path(child)
all_paths.extend(path)
max_skill = max(max_skill, skill)
return all_paths, max_skill
Identifying Critical Nodes
Nodes that appear in all attack paths are critical—hardening them blocks multiple attacks:
def find_all_paths(node, current_path=None):
"""Generate all possible attack paths."""
current_path = current_path or []
if node.get('leaf'):
yield current_path + [node['name']]
return
if node['type'] == 'OR':
for child in node['children']:
yield from find_all_paths(child, current_path + [node['name']])
else: # AND
# For AND, we need all children in the path
child_paths = [list(find_all_paths(c, [])) for c in node['children']]
# Combine all possibilities (Cartesian product)
from itertools import product
for combo in product(*child_paths):
combined = current_path + [node['name']]
for path in combo:
combined.extend(path)
yield combined
def find_critical_nodes(attack_tree):
"""Find nodes that appear in ALL attack paths."""
paths = list(find_all_paths(attack_tree))
if not paths:
return set()
# Start with all nodes in first path
critical = set(paths[0])
# Intersect with all other paths
for path in paths[1:]:
critical &= set(path)
return critical
Attack Trees for Common Scenarios
Example 1: Data Exfiltration
Goal: Exfiltrate Customer Database
OR
|-- External Attack
| OR
| |-- Exploit Public-Facing App
| | AND
| | |-- Find RCE Vulnerability
| | |-- Establish Reverse Shell
| | |-- Locate Database
| | |-- Exfil via Encrypted Channel
| |
| |-- Compromise Cloud Account
| AND
| |-- Obtain AWS Keys (leaked in repo)
| |-- Access S3 Backup Bucket
| |-- Download Database Dump
|
|-- Insider Threat
| OR
| |-- Malicious Admin
| | AND
| | |-- Admin Exports Data
| | |-- Transfers to Personal Device
| |
| |-- Compromised Endpoint
| AND
| |-- Malware on Employee Laptop
| |-- Intercept Database Credentials
| |-- Connect via VPN
| |-- Query and Exfil Data
|
|-- Physical Access
AND
|-- Gain Building Access
|-- Find Unattended Workstation
|-- Connect USB Device
|-- Copy Database
Example 2: Supply Chain Attack
Goal: Inject Malicious Code into Product
OR
|-- Compromise Source Repository
| OR
| |-- Steal Developer Credentials
| | AND
| | |-- Phish Developer
| | |-- Bypass MFA (SIM swap/push fatigue)
| | |-- Push Malicious Commit
| |
| |-- Compromise CI/CD Pipeline
| AND
| |-- Access Jenkins/GitHub Actions
| |-- Modify Build Script
| |-- Inject During Build
|
|-- Compromise Dependency
| OR
| |-- Typosquatting
| | AND
| | |-- Register Similar Package Name
| | |-- Developer Makes Typo
| |
| |-- Compromise Legitimate Package
| AND
| |-- Take Over Maintainer Account
| |-- Push Malicious Update
|
|-- Compromise Build Infrastructure
AND
|-- Access Build Server
|-- Modify Compiler/Toolchain
|-- Inject at Compile Time
Integrating Attack Trees with STRIDE/DREAD
Use all three methodologies together:
- STRIDE: Categorize what types of threats exist
- Attack Trees: Model how each threat could be realized
- DREAD: Prioritize which attack paths to defend first
# Combined threat model
threat = {
'name': 'Admin Account Compromise',
'stride_category': 'Spoofing / Elevation of Privilege',
'attack_tree': attack_tree, # From above
'dread': {
'damage': 9,
'reproducibility': 7,
'exploitability': 6,
'affected_users': 10,
'discoverability': 5
},
'analysis': {
'cheapest_path': 'SQL Injection',
'min_cost': 0,
'easiest_path': 'Phishing',
'critical_controls': [
'MFA on admin accounts',
'Input validation',
'Security awareness training'
]
}
}
Tools for Attack Tree Modeling
Text-Based Tools
- PlantUML: Generate diagrams from text
- Mermaid: Markdown-compatible diagrams
- Graphviz: Powerful graph visualization
Mermaid Example
graph TD
A[Gain Admin Access] --> B[Steal Credentials]
A --> C[Exploit Vulnerability]
A --> D[Social Engineering]
B --> B1[Phishing]
B --> B2[Credential Stuffing]
B --> B3[Keylogger]
C --> C1[SQL Injection]
C --> C2[Auth Bypass]
D --> D1[Helpdesk Call]
D --> D2[Physical Access]
Dedicated Attack Tree Tools
- ADTool: Academic tool for attack-defense trees
- Threagile: Code-based threat modeling
- Microsoft Threat Modeling Tool: Free GUI tool
- OWASP Threat Dragon: Open-source web app
Best Practices
Do:
- Start with a clear, specific goal
- Involve multiple perspectives (dev, ops, security)
- Update trees as the system evolves
- Focus on realistic attack scenarios
- Annotate with costs and probabilities
Don't:
- Make trees too detailed (keep manageable)
- Forget about insider threats
- Ignore physical attack vectors
- Stop at theoretical—validate with pen testing
- Create once and never update
Summary
Attack trees provide a visual, systematic way to model threats:
- Root node: What does the attacker want?
- Child nodes: How can they achieve it?
- OR gates: Attacker needs any one path
- AND gates: Attacker needs all sub-steps
- Annotations: Cost, skill, probability for analysis
Use attack trees to:
- Discover attack paths you hadn't considered
- Identify critical controls that block multiple paths
- Communicate threats to non-technical stakeholders
- Prioritize security investments
In the next section, we'll apply everything we've learned with hands-on threat modeling exercises.
Found an issue?