Skip to content

Visualizing with MITRE ATT&CK Navigator: How to Visualize Mapped Data in MITRE ATT&CK Navigator

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.

MITRE ATT&CK Navigator for Enterprise

 

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

Groups Coverage Heatmap

Our first visualization shows which techniques are most commonly used by threat groups. Some interesting findings:

Mitigations Coverage

Mitigations Coverage Heatmap

The mitigations heatmap reveals some interesting patterns:

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

  1. 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
      ])))
    
  2. 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
        }
  3. 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:

  1. Filtering Groups by Sector
    • Identifying groups known to target financial institutions
    • Analyzing their common techniques
    • Understanding their typical attack patterns
  2. Creating Custom Layers
    • Building sector-specific visualizations
    • Highlighting relevant techniques
    • Mapping existing security controls
  3. 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