Hey there, fellow threat hunters! 👋 Welcome to part 3 of our MITRE ATT&CK journey! In our previous posts, we covered data retrieval and relationship mapping. Today, we're diving into something visually exciting - analyzing and visualizing our MITRE ATT&CK data using the MITRE ATT&CK Navigator.
The Story Our Data Tells
Let's start by looking at some interesting statistics our analyzer uncovered:
2024-12-22 14:09:46,402 - __main__ - INFO - Statistics for techniques:
2024-12-22 14:09:46,402 - __main__ - INFO - all_techniques: 799
2024-12-22 14:09:46,402 - __main__ - INFO - total_used_techniques: 799
2024-12-22 14:09:46,402 - __main__ - INFO - total_groups: 3969
2024-12-22 14:09:46,402 - __main__ - INFO - total_mitigations: 1372
2024-12-22 14:09:46,402 - __main__ - INFO - total_references: 3961
2024-12-22 14:09:46,403 - __main__ - INFO - avg_groups_per_technique: 4.97
2024-12-22 14:09:46,403 - __main__ - INFO - avg_mitigations_per_technique: 1.72
2024-12-22 14:09:46,403 - __main__ - INFO - avg_references_per_technique: 4.96
Those are some impressive numbers! But raw numbers don't tell the whole story. Let's see how our analyzer helps us make sense of it all.
The Analysis Engine
Our analyzer.py is the brain behind our visualization operation. Here's how it works:
1. Statistical Analysis
First, we calculate comprehensive statistics for our techniques:
def analyze_and_update_techniques(techniques: List[Dict[str, Any]],
all_techniques_length: int) -> tuple[Dict[str, Any],
List[Dict[str, Any]]]:
for technique in techniques:
technique['stats'] = {
'groups_count': len(technique.get('groups', [])),
'mitigations_count': len(technique.get('mitigations', [])),
'referenced_count': len(technique.get('external_references', []))
}
2. Dynamic Color Generation
One cool feature is our dynamic color gradient generation for heatmaps:
def generate_color_gradient(start_hex: str, end_hex: str, steps: int) -> List[str]:
def hex_to_hsv(hex_color: str) -> Tuple[float, float, float]:
hex_color = hex_color.lstrip('#')
rgb = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
return colorsys.rgb_to_hsv(rgb[0]/255, rgb[1]/255, rgb[2]/255)
start_hsv = hex_to_hsv(start_hex)
end_hsv = hex_to_hsv(end_hex)
gradient = []
for i in range(steps):
ratio = i / (steps - 1)
hsv = tuple(
start + (end - start) * ratio
for start, end in zip(start_hsv, end_hsv)
)
gradient.append(hsv_to_hex(hsv))
return gradient
Creating Navigator Layers
The real magic happens when we create our Navigator layers. These visualizations help us understand our data at a glance. The MITRE ATT&CK Navigator is a powerful web-based tool that helps us visualize and annotate ATT&CK matrices.
Our code creates three different types of layer files:
- Groups Layer: Shows how many groups use each technique
- Mitigations Layer: Displays mitigation coverage
- References Layer: Indicates how well-documented each technique is
Here's how we create these layers:
def create_navigator_layer(techniques: List[Dict[str, Any]],
layer_name: str,
count_type: str,
hide_uncovered=True) -> Dict[str, Any]:
# Calculate color thresholds based on data distribution
colors = calculate_color_thresholds(techniques, count_type)
# Basic layer structure required by Navigator
result_data = {
"description": f"Enterprise techniques heat map showing {count_type} count",
"name": layer_name,
"domain": "enterprise-attack",
"versions": {
"attack": "16",
"navigator": "5.0.0",
"layer": "4.5"
},
"gradient": {
"colors": [],
"minValue": 0,
"maxValue": 1
},
"legendItems": [],
"techniques": [],
"showTacticRowBackground": True,
"tacticRowBackground": "#dddddd",
"selectTechniquesAcrossTactics": True,
"selectSubtechniquesWithParent": True,
"selectVisibleTechniques": False,
"layout": {
"layout": "flat",
"showName": True,
"showID": False,
"expandedSubtechniques": True
},
"hideDisabled": True # Make sure to hide disabled techniques for better overview.
}
For each technique, we add detailed information to our layer:
# Adding technique entries
for technique in techniques:
if not technique.get("technique_id"):
logger.debug(f"Could not find technique_id for {technique.get('name', 'Unknown')}")
continue
count = technique['stats'].get(f'{count_type}_count', 0)
comment = f"{count} {count_type}"
color = find_color_for_count(colors, count)
technique_entry = {
"techniqueID": technique["technique_id"],
"color": color,
"comment": comment,
"showSubtechniques": True,
"enabled": not hide_uncovered or count > 0,
}
result_data["techniques"].append(technique_entry)
Finally, we add a gradient legend to help interpret the visualization:
# Add color gradient and legend
result_data["gradient"]["colors"] = [v['color'] for v in colors.values()]
result_data["legendItems"] = [
{
"label": "More" if k == "more" else str(k),
"color": v['color']
} for k, v in colors.items()
]
We save these layers as separate JSON files that can be imported directly into the MITRE ATT&CK Navigator:
def save_navigator_layers(analysis_results: Dict[str, Any],
output_dir: str,
hide_uncovered=True) -> None:
os.makedirs(output_dir, exist_ok=True)
layer_types = {
'groups': 'Groups Heat Map',
'mitigations': 'Mitigations Heat Map',
'references': 'References Heat Map'
}
for layer_type, layer_name in layer_types.items():
layer = create_navigator_layer(
analysis_results['techniques'],
layer_name,
layer_type,
hide_uncovered=hide_uncovered
)
output_path = os.path.join(output_dir, f'{layer_type}_layer.json')
with open(output_path, 'w') as f:
json.dump(layer, f, indent=2)
logger.info(f"Saved {layer_type} layer to {output_path}")
These layer files can then be loaded into the MITRE ATT&CK Navigator web interface (https://mitre-attack.github.io/attack-navigator/), giving us beautiful visualizations of our data. Each layer provides different insights:
- Groups Layer: Darker colors indicate techniques used by more groups, helping identify commonly used tactics
- Mitigations Layer: Shows which techniques have more or fewer documented mitigations, highlighting potential defensive gaps
- References Layer: Indicates which techniques are well-documented vs. those that might need more research
Understanding the Visualizations
Groups Coverage Heatmap
Our first visualization shows which techniques are most commonly used by threat groups. Some interesting findings:
- T1059.001 (PowerShell): Used by 76 groups
- T1204.002 (Malicious File): Used by 82 groups
- T1566.001 (Spearphishing Attachment): Used by 77 groups
Mitigations Coverage
The mitigations heatmap reveals some interesting patterns:
- T1552 (Unsecured Credentials): 11 mitigations
- T1072 (Software Deployment Tools): 10 mitigations
- Some heavily-used techniques have surprisingly few documented mitigations
Deep Dive into the Statistics
Our analyzer calculates some fascinating metrics:
overall_stats = { "all_techniques": all_techniques_length, 'total_used_techniques': total_techniques, 'total_groups': sum(t['stats']['groups_count'] for t in techniques), 'total_mitigations': sum(t['stats']['mitigations_count'] for t in techniques), 'avg_groups_per_technique': safe_average([t['stats']['groups_count'] for t in techniques], total_techniques), # ... more statistics ... }
Pro Tips for Analysis
- Color Distribution: Use quantiles for better color distribution in heatmaps:
quarts = quantiles(counts, n=4) thresholds = sorted(list(set([ 0, round(quarts[0]), round(mean(counts)), round(quarts[2]), round(max_count * 0.9), max_count ])))
- Handle Edge Cases: Always provide default colors for edge cases:
def default_color_scheme() -> Dict[str, Dict[str, str]]: return { "0": {"color": "#ffffff"}, # White "1": {"color": "#ff6666"}, # Light red "more": {"color": "#2b0000"} # Very deep red }
- Save Your Work: Always save your layers for future reference:
def save_navigator_layers(analysis_results: Dict[str, Any], output_dir: str, hide_uncovered=True) -> None: os.makedirs(output_dir, exist_ok=True) # ... save layers ...
Making the Most of Your Analysis
Here are some practical ways to use this analysis:
1. Defense Planning
- Identify techniques with high group usage but low mitigation coverage
- Focus on implementing mitigations for frequently used techniques
- Track your defensive coverage over time
2. Threat Intelligence
- Identify trending techniques among threat groups
- Spot gaps in your defensive strategy
- Prioritize your security investments
Common Analysis Pitfalls
- Data Freshness: Always check your MITRE ATT&CK data version
- Context Matters: Not all techniques are equally relevant to your environment
- Color Schemes: Choose colors that make sense for your audience
- Scale Considerations: Be careful with color gradient steps - too many or too few can be misleading
What's Next?
In our next post, we'll make this analysis more actionable by focusing on a specific use case: identifying and analyzing relevant threat groups for a financial services organization. We'll cover:
- Filtering Groups by Sector
- Identifying groups known to target financial institutions
- Analyzing their common techniques
- Understanding their typical attack patterns
- Creating Custom Layers
- Building sector-specific visualizations
- Highlighting relevant techniques
- Mapping existing security controls
- Practical Application
- Developing focused detection strategies
- Prioritizing security investments
Getting Started
Want to try this yourself? Here's how:
# Clone the repository
git clone https://github.com/mitre-attack/mitreattack-python.git
cd mitre
# Install requirements
pip install -r requirements.txt
# Run the analysis
python main.py
Check out Parts 1 and 2 if you haven't already - they'll help you understand how we got here!
Final Thoughts
Remember, visualization isn't just about making pretty pictures - it's about making data actionable. Use these tools to understand your threat landscape better and make informed security decisions.
Stay tuned for more security scripting adventures! And remember - sometimes the best insights come from just visualizing your data differently! 🕵️♂️
Until next time, happy hunting!
References
- MITRE ATT&CK Framework
- MITRE ATT&CK Navigator
- Cyberchef
- NIST CSF
0 comments:
Post a Comment