MITRE ATT&CK python library for accessing, querying, and manipulating ATT&CK threat intelligence data.
—
Compare different versions of ATT&CK data and generate detailed changelog reports showing additions, modifications, and removals between releases. This module provides comprehensive tools for analyzing changes in the ATT&CK framework over time, tracking the evolution of techniques, groups, software, and relationships.
Core classes for comparing ATT&CK versions and generating change reports.
class DiffStix:
def __init__(self, old_data: dict, new_data: dict):
"""
Initialize ATT&CK version comparison.
Args:
old_data (dict): Earlier version STIX data bundle
new_data (dict): Later version STIX data bundle
"""
def generate_changelog(self) -> dict:
"""
Generate comprehensive changelog between versions.
Returns:
dict: Structured changelog with additions, modifications, and removals
"""
def export_changelog(self, output_file: str, format: str = "json") -> None:
"""
Export changelog to file.
Args:
output_file (str): Path to output file
format (str): Output format ("json", "markdown", "html")
"""
def get_added_objects(self, object_type: str = None) -> List[dict]:
"""
Get objects added in the new version.
Args:
object_type (str, optional): Filter by STIX object type
Returns:
List[dict]: List of added objects
"""
def get_removed_objects(self, object_type: str = None) -> List[dict]:
"""
Get objects removed in the new version.
Args:
object_type (str, optional): Filter by STIX object type
Returns:
List[dict]: List of removed objects
"""
def get_modified_objects(self, object_type: str = None) -> List[dict]:
"""
Get objects modified between versions.
Args:
object_type (str, optional): Filter by STIX object type
Returns:
List[dict]: List of modified objects with change details
"""
def compare_relationships(self) -> dict:
"""
Compare relationships between versions.
Returns:
dict: Changes in relationships (added, removed, modified)
"""
def generate_summary_statistics(self) -> dict:
"""
Generate summary statistics of changes.
Returns:
dict: Summary counts of additions, modifications, removals by type
"""Support classes for tracking object versions and changes.
class AttackObjectVersion:
def __init__(self, stix_object: dict, version_info: dict):
"""
Track version information for an ATT&CK object.
Args:
stix_object (dict): STIX object data
version_info (dict): Version metadata
"""
def get_version_string(self) -> str:
"""
Get version string for the object.
Returns:
str: Version identifier
"""
def compare_with(self, other: 'AttackObjectVersion') -> dict:
"""
Compare this version with another version of the same object.
Args:
other (AttackObjectVersion): Other version to compare with
Returns:
dict: Detailed comparison results
"""
def get_field_changes(self, other: 'AttackObjectVersion') -> Dict[str, dict]:
"""
Get field-level changes between versions.
Args:
other (AttackObjectVersion): Other version to compare with
Returns:
Dict[str, dict]: Field changes with old and new values
"""Specialized JSON encoder for serializing change data.
class AttackChangesEncoder(json.JSONEncoder):
def default(self, obj):
"""
Custom JSON encoder for ATT&CK change objects.
Args:
obj: Object to encode
Returns:
Serializable representation of the object
"""Command-line interface for version comparison workflows.
def main(args: List[str] = None) -> None:
"""
CLI entry point for diff_stix command.
Args:
args (List[str], optional): Command-line arguments
--old: Path to older version STIX file
--new: Path to newer version STIX file
--output: Output file path for changelog
--format: Output format (json, markdown, html)
--filter: Filter by object type
--summary-only: Generate summary statistics only
"""import json
from mitreattack.diffStix import DiffStix
# Load two versions of ATT&CK data
with open("enterprise-attack-v14.1.json", "r") as f:
old_data = json.load(f)
with open("enterprise-attack-v15.0.json", "r") as f:
new_data = json.load(f)
# Initialize comparison
diff_analyzer = DiffStix(old_data, new_data)
# Generate changelog
changelog = diff_analyzer.generate_changelog()
# Export to file
diff_analyzer.export_changelog("v14.1_to_v15.0_changelog.json", format="json")
diff_analyzer.export_changelog("v14.1_to_v15.0_changelog.md", format="markdown")
print("Changelog generated successfully")from mitreattack.diffStix import DiffStix
import json
# Load version data
with open("old_version.json", "r") as f:
old_data = json.load(f)
with open("new_version.json", "r") as f:
new_data = json.load(f)
diff_analyzer = DiffStix(old_data, new_data)
# Get added techniques
added_techniques = diff_analyzer.get_added_objects("attack-pattern")
print(f"Added techniques: {len(added_techniques)}")
for technique in added_techniques[:5]: # Show first 5
print(f" - {technique.get('name', 'Unknown')} ({technique.get('external_references', [{}])[0].get('external_id', 'No ID')})")
# Get removed groups
removed_groups = diff_analyzer.get_removed_objects("intrusion-set")
print(f"\\nRemoved groups: {len(removed_groups)}")
# Get modified software
modified_software = diff_analyzer.get_modified_objects("malware")
modified_tools = diff_analyzer.get_modified_objects("tool")
print(f"\\nModified software: {len(modified_software + modified_tools)}")
# Relationship changes
relationship_changes = diff_analyzer.compare_relationships()
print(f"\\nRelationship changes:")
print(f" Added: {len(relationship_changes.get('added', []))}")
print(f" Removed: {len(relationship_changes.get('removed', []))}")
print(f" Modified: {len(relationship_changes.get('modified', []))}")from mitreattack.diffStix import DiffStix
import json
# Perform comparison
diff_analyzer = DiffStix(old_data, new_data)
# Generate summary statistics
summary = diff_analyzer.generate_summary_statistics()
print("ATT&CK Version Comparison Summary")
print("=" * 40)
for object_type, changes in summary.items():
if isinstance(changes, dict):
print(f"{object_type.upper()}:")
print(f" Added: {changes.get('added', 0)}")
print(f" Removed: {changes.get('removed', 0)}")
print(f" Modified: {changes.get('modified', 0)}")
print()
# Export summary as JSON
with open("version_comparison_summary.json", "w") as f:
json.dump(summary, f, indent=2, cls=AttackChangesEncoder)from mitreattack.diffStix import DiffStix, AttackObjectVersion
import json
diff_analyzer = DiffStix(old_data, new_data)
# Get modified techniques for detailed analysis
modified_techniques = diff_analyzer.get_modified_objects("attack-pattern")
print(f"Analyzing {len(modified_techniques)} modified techniques...")
for modified_technique in modified_techniques[:3]: # Analyze first 3
technique_id = modified_technique.get('external_references', [{}])[0].get('external_id', 'Unknown')
technique_name = modified_technique.get('name', 'Unknown')
print(f"\\nTechnique: {technique_name} ({technique_id})")
# Get detailed field changes
if 'changes' in modified_technique:
changes = modified_technique['changes']
for field, change_info in changes.items():
if isinstance(change_info, dict) and 'old' in change_info and 'new' in change_info:
old_value = change_info['old']
new_value = change_info['new']
# Truncate long values for display
if len(str(old_value)) > 100:
old_value = str(old_value)[:97] + "..."
if len(str(new_value)) > 100:
new_value = str(new_value)[:97] + "..."
print(f" {field}:")
print(f" Old: {old_value}")
print(f" New: {new_value}")# Compare two specific versions
diff_stix --old enterprise-attack-v14.1.json --new enterprise-attack-v15.0.json --output changelog.json
# Generate markdown changelog
diff_stix --old old_version.json --new new_version.json --output changelog.md --format markdown
# Filter by specific object type
diff_stix --old old_version.json --new new_version.json --filter attack-pattern --output technique_changes.json
# Generate summary only
diff_stix --old old_version.json --new new_version.json --summary-only --output summary.json
# Compare with HTML output
diff_stix --old old_version.json --new new_version.json --format html --output changes_report.htmlimport json
from pathlib import Path
from mitreattack.diffStix import DiffStix
from mitreattack.download_stix import download_stix, get_available_versions
from mitreattack.release_info import STIX20
def track_attack_evolution(versions: List[str], domain: str = "enterprise-attack"):
"""
Track ATT&CK evolution across multiple versions.
"""
download_dir = Path("./version_tracking/")
download_dir.mkdir(exist_ok=True)
# Download all requested versions
version_files = {}
for version in versions:
file_path = download_stix(
stix_version="2.0",
domain=domain,
download_dir=str(download_dir),
release=version,
known_hash=STIX20[version]
)
version_files[version] = file_path
# Compare consecutive versions
evolution_report = {"comparisons": []}
for i in range(len(versions) - 1):
old_version = versions[i]
new_version = versions[i + 1]
print(f"Comparing {old_version} -> {new_version}")
# Load data
with open(version_files[old_version], "r") as f:
old_data = json.load(f)
with open(version_files[new_version], "r") as f:
new_data = json.load(f)
# Perform comparison
diff_analyzer = DiffStix(old_data, new_data)
summary = diff_analyzer.generate_summary_statistics()
comparison_result = {
"from_version": old_version,
"to_version": new_version,
"summary": summary
}
evolution_report["comparisons"].append(comparison_result)
# Export detailed changelog
changelog_file = download_dir / f"{old_version}_to_{new_version}_changelog.json"
diff_analyzer.export_changelog(str(changelog_file), format="json")
# Save evolution report
with open(download_dir / "evolution_report.json", "w") as f:
json.dump(evolution_report, f, indent=2)
print(f"Evolution tracking complete. Reports saved to {download_dir}")
return evolution_report
# Track evolution across recent versions
recent_versions = ["13.1", "14.0", "14.1", "15.0", "15.1"]
evolution_data = track_attack_evolution(recent_versions)
# Print summary
for comparison in evolution_data["comparisons"]:
from_ver = comparison["from_version"]
to_ver = comparison["to_version"]
summary = comparison["summary"]
print(f"\\n{from_ver} -> {to_ver}:")
total_added = sum(stats.get("added", 0) for stats in summary.values() if isinstance(stats, dict))
total_modified = sum(stats.get("modified", 0) for stats in summary.values() if isinstance(stats, dict))
total_removed = sum(stats.get("removed", 0) for stats in summary.values() if isinstance(stats, dict))
print(f" Total changes: +{total_added} / ~{total_modified} / -{total_removed}")from mitreattack.diffStix import DiffStix
import json
def analyze_technique_changes(old_data: dict, new_data: dict):
"""
Custom analysis focusing on technique changes.
"""
diff_analyzer = DiffStix(old_data, new_data)
# Get technique changes
added_techniques = diff_analyzer.get_added_objects("attack-pattern")
modified_techniques = diff_analyzer.get_modified_objects("attack-pattern")
removed_techniques = diff_analyzer.get_removed_objects("attack-pattern")
analysis_results = {
"new_techniques": [],
"updated_techniques": [],
"deprecated_techniques": [],
"platform_changes": {},
"tactic_changes": {}
}
# Analyze added techniques
for technique in added_techniques:
tech_id = technique.get('external_references', [{}])[0].get('external_id', 'Unknown')
tech_name = technique.get('name', 'Unknown')
platforms = technique.get('x_mitre_platforms', [])
analysis_results["new_techniques"].append({
"id": tech_id,
"name": tech_name,
"platforms": platforms
})
# Analyze modified techniques for platform changes
for technique in modified_techniques:
if 'changes' in technique and 'x_mitre_platforms' in technique['changes']:
tech_id = technique.get('external_references', [{}])[0].get('external_id', 'Unknown')
old_platforms = technique['changes']['x_mitre_platforms'].get('old', [])
new_platforms = technique['changes']['x_mitre_platforms'].get('new', [])
added_platforms = set(new_platforms) - set(old_platforms)
removed_platforms = set(old_platforms) - set(new_platforms)
if added_platforms or removed_platforms:
analysis_results["platform_changes"][tech_id] = {
"added_platforms": list(added_platforms),
"removed_platforms": list(removed_platforms)
}
return analysis_results
# Perform custom analysis
with open("old_attack.json", "r") as f:
old_data = json.load(f)
with open("new_attack.json", "r") as f:
new_data = json.load(f)
custom_analysis = analyze_technique_changes(old_data, new_data)
print(f"New techniques: {len(custom_analysis['new_techniques'])}")
print(f"Platform changes: {len(custom_analysis['platform_changes'])}")
# Show platform changes
for tech_id, changes in custom_analysis["platform_changes"].items():
if changes["added_platforms"]:
print(f"{tech_id}: Added platforms: {', '.join(changes['added_platforms'])}")
if changes["removed_platforms"]:
print(f"{tech_id}: Removed platforms: {', '.join(changes['removed_platforms'])}")Install with Tessl CLI
npx tessl i tessl/pypi-mitreattack-python