2024-06-17 10:01:26 +02:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
2024-06-18 14:30:39 +02:00
# A simple convertor of the MITRE FiGHT to a MISP Galaxy datastructure.
2024-06-17 10:01:26 +02:00
# Copyright (C) 2024 Christophe Vandeplas
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2024-06-17 15:05:35 +02:00
from bs4 import BeautifulSoup
from markdown import markdown
2024-06-17 10:01:26 +02:00
import json
import os
2024-06-17 15:05:35 +02:00
import re
2024-06-17 10:01:26 +02:00
import requests
import uuid
import yaml
2024-06-18 14:30:39 +02:00
from pymispgalaxies import Cluster , Galaxy
2024-06-17 10:01:26 +02:00
uuid_seed = ' 8666d04b-977a-434b-82b4-f36271ec1cfb '
fight_url = ' https://fight.mitre.org/fight.yaml '
2024-06-18 14:30:39 +02:00
galaxy_type = " mitre-fight "
galaxy_description = ' MITRE Five-G Hierarchy of Threats (FiGHT™) is a globally accessible knowledge base of adversary tactics and techniques that are used or could be used against 5G networks. '
galaxy_source = ' https://fight.mitre.org/ '
2024-06-17 10:01:26 +02:00
r = requests . get ( fight_url )
fight = yaml . safe_load ( r . text )
# with open('fight.yaml', 'w') as f:
# f.write(r.text)
# with open('fight.yaml', 'r') as f:
# fight = yaml.safe_load(f)
2024-06-18 14:30:39 +02:00
mitre_attack_pattern = Cluster ( ' mitre-attack-pattern ' )
2024-06-17 15:05:35 +02:00
def find_mitre_uuid_from_technique_id ( technique_id ) :
2024-06-18 14:30:39 +02:00
try :
return mitre_attack_pattern . get_by_external_id ( technique_id ) . uuid
except KeyError :
print ( " No MITRE UUID found for technique_id: " , technique_id )
return None
2024-06-17 15:05:35 +02:00
2024-06-17 10:01:26 +02:00
def clean_ref ( text : str ) - > str :
'''
' <a name= " 1 " > \\ [1 \\ ] </a> [5GS Roaming Guidelines Version 5.0 (non-confidential), NG.113-v5.0, GSMA, December 2021](https://www.gsma.com/newsroom/wp-content/uploads//NG.113-v5.0.pdf) '
'''
html = markdown ( text . replace ( ' ]( ' , ' - ' ) . replace ( ' ) ' , ' ' ) . replace ( ' [ ' , ' ' ) )
soup = BeautifulSoup ( html , ' html.parser ' )
return soup . get_text ( ) . strip ( )
2024-06-17 12:21:12 +02:00
def save_galaxy_and_cluster ( json_galaxy , json_cluster , galaxy_fname ) :
# save the Galaxy and Cluster file
with open ( os . path . join ( ' .. ' , ' galaxies ' , galaxy_fname ) , ' w ' ) as f :
# sort_keys, even if it breaks the kill_chain_order , but jq_all_the_things requires sorted keys
json . dump ( json_galaxy , f , indent = 2 , sort_keys = True , ensure_ascii = False )
f . write ( ' \n ' ) # only needed for the beauty and to be compliant with jq_all_the_things
with open ( os . path . join ( ' .. ' , ' clusters ' , galaxy_fname ) , ' w ' ) as f :
json . dump ( json_cluster , f , indent = 2 , sort_keys = True , ensure_ascii = False )
f . write ( ' \n ' ) # only needed for the beauty and to be compliant with jq_all_the_things
2024-06-17 10:01:26 +02:00
# tactics
2024-06-18 14:30:39 +02:00
tactics = { } # key = ID, value = tactic
2024-06-17 10:01:26 +02:00
for item in fight [ ' tactics ' ] :
tactics [ item [ ' id ' ] ] = item [ ' name ' ] . replace ( ' ' , ' - ' )
2024-06-18 14:30:39 +02:00
#
2024-06-17 10:01:26 +02:00
# techniques
2024-06-18 14:30:39 +02:00
#
technique_galaxy_name = " MITRE FiGHT Techniques "
technique_cluster = Cluster ( {
' authors ' : [ " MITRE " ] ,
' category ' : ' attack-pattern ' ,
' name ' : technique_galaxy_name ,
' description ' : galaxy_description ,
' source ' : galaxy_source ,
' type ' : galaxy_type ,
' uuid ' : " 6a1fa29f-85a5-4b1c-956b-ebb7df314486 " ,
' version ' : 1
} )
2024-06-17 15:18:55 +02:00
2024-06-17 10:01:26 +02:00
for item in fight [ ' techniques ' ] :
2024-06-17 15:18:55 +02:00
technique_string = item [ ' name ' ] . strip ( ) . lower ( )
2024-06-17 12:21:12 +02:00
element = {
' value ' : item [ ' name ' ] . strip ( ) ,
' description ' : item [ ' description ' ] . strip ( ) ,
2024-06-17 10:01:26 +02:00
' uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , item [ ' id ' ] ) ) ,
' meta ' : {
' kill_chain ' : [ ] ,
' refs ' : [ f " https://fight.mitre.org/techniques/ { item [ ' id ' ] } " ] ,
' external_id ' : item [ ' id ' ]
} ,
' related ' : [ ]
}
2024-06-17 15:05:35 +02:00
keys_to_skip = [ ' id ' , ' name ' , ' references ' , ' tactics ' , ' description ' ]
2024-06-17 10:01:26 +02:00
for keys in item . keys ( ) :
if keys not in keys_to_skip :
2024-06-17 12:21:12 +02:00
element [ ' meta ' ] [ keys ] = item [ keys ]
2024-06-17 10:01:26 +02:00
2024-06-17 15:05:35 +02:00
if ' https://attack.mitre.org/techniques/ ' in item [ ' description ' ] :
# extract the references from the description
# add it as ref and build the relationship to the technique using uuid
url = re . search ( r ' (https?://[^ \ )]+)/(T[^ \ )]+) ' , item [ ' description ' ] )
if url :
extracted_url = url . group ( 0 )
element [ ' meta ' ] [ ' refs ' ] . append ( extracted_url )
technique_uuid = find_mitre_uuid_from_technique_id ( url . group ( 2 ) . replace ( ' / ' , ' . ' ) )
if technique_uuid :
element [ ' related ' ] . append ( {
' dest-uuid ' : technique_uuid ,
' type ' : ' related-to '
} )
else :
print ( " WARNING: No MITRE UUID found for technique_id: " , url . group ( 2 ) )
pass
2024-06-17 10:01:26 +02:00
try :
for ref in item [ ' references ' ] :
2024-06-17 12:21:12 +02:00
element [ ' meta ' ] [ ' refs ' ] . append ( clean_ref ( ref ) )
2024-06-17 10:01:26 +02:00
except KeyError :
pass
for tactic in item [ ' tactics ' ] :
2024-06-17 12:21:12 +02:00
element [ ' meta ' ] [ ' kill_chain ' ] . append ( f " fight: { tactics [ tactic ] } " )
2024-06-17 10:01:26 +02:00
for mitigation in item [ ' mitigations ' ] :
2024-06-17 12:21:12 +02:00
element [ ' meta ' ] [ ' refs ' ] . append ( f " https://fight.mitre.org/mitigations/ { mitigation [ ' fgmid ' ] } " )
2024-06-17 10:01:26 +02:00
# add relationship
2024-06-17 12:21:12 +02:00
element [ ' related ' ] . append ( {
2024-06-17 10:01:26 +02:00
' dest-uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , mitigation [ ' fgmid ' ] ) ) ,
' type ' : ' mitigated-by '
} )
for detection in item [ ' detections ' ] :
2024-06-17 12:21:12 +02:00
element [ ' meta ' ] [ ' refs ' ] . append ( f " https://fight.mitre.org/data%20sources/ { detection [ ' fgdsid ' ] } " )
2024-06-17 10:01:26 +02:00
# add relationship
2024-06-17 12:21:12 +02:00
element [ ' related ' ] . append ( {
2024-06-17 10:01:26 +02:00
' dest-uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , detection [ ' fgdsid ' ] ) ) ,
' type ' : ' detected-by '
} )
try :
2024-06-17 12:21:12 +02:00
element [ ' related ' ] . append ( {
2024-06-17 10:01:26 +02:00
' dest-uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , item [ ' subtechnique-of ' ] ) ) ,
' type ' : ' subtechnique-of '
} )
except KeyError :
pass
2024-06-17 12:27:17 +02:00
element [ ' meta ' ] [ ' refs ' ] = list ( set ( element [ ' meta ' ] [ ' refs ' ] ) )
element [ ' meta ' ] [ ' refs ' ] . sort ( )
2024-06-18 14:30:39 +02:00
technique_cluster . append ( element , skip_duplicates = True )
technique_cluster . save ( ' mitre-fight-techniques ' )
for cluster , duplicate in technique_cluster . duplicates :
print ( f " Skipped duplicate: { duplicate } in cluster { cluster } " )
kill_chain_tactics = technique_cluster . get_kill_chain_tactics ( )
try :
technique_galaxy = Galaxy ( ' mitre-fight-techniques ' )
# check if new kill_chain_tactics are present, add them if needed
for key , values in kill_chain_tactics . items ( ) :
if key not in technique_galaxy . kill_chain_order :
technique_galaxy . kill_chain_order [ key ] = [ ]
for value in values :
if key not in technique_galaxy . kill_chain_order :
print ( f " New kill_chain_tactic found: { key } : { value } " )
technique_galaxy . kill_chain_order . append ( tactic )
2024-06-24 11:05:13 +02:00
except ( KeyError , FileNotFoundError ) :
2024-06-18 14:30:39 +02:00
technique_galaxy = Galaxy ( {
' description ' : galaxy_description ,
' icon ' : " map " ,
' kill_chain_order ' : kill_chain_tactics ,
' name ' : technique_galaxy_name ,
' namespace ' : " mitre " ,
' type ' : galaxy_type ,
' uuid ' : " c22c8c18-0ccd-4033-b2dd-804ad26af4b9 " ,
' version ' : 1
} )
2024-06-17 10:01:26 +02:00
2024-06-18 14:30:39 +02:00
technique_galaxy . save ( ' mitre-fight-techniques ' )
2024-06-17 10:01:26 +02:00
2024-06-18 14:30:39 +02:00
#
2024-06-17 12:21:12 +02:00
# mitigations
2024-06-18 14:30:39 +02:00
#
mitigation_galaxy_name = " MITRE FiGHT Mitigations "
mitigation_cluster = Cluster ( {
' authors ' : [ " MITRE " ] ,
' category ' : ' mitigation ' ,
' name ' : mitigation_galaxy_name ,
' description ' : galaxy_description ,
' source ' : galaxy_source ,
' type ' : galaxy_type ,
' uuid ' : " fe20707f-2dfb-4436-8520-8fedb8c79668 " ,
' version ' : 1
} )
2024-06-17 12:21:12 +02:00
for item in fight [ ' mitigations ' ] :
element = {
' value ' : item [ ' name ' ] . strip ( ) ,
' description ' : item [ ' description ' ] . strip ( ) ,
' uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , item [ ' id ' ] ) ) ,
' meta ' : {
' kill_chain ' : [ ] ,
' refs ' : [ f " https://fight.mitre.org/mitigations/ { item [ ' id ' ] } " ] ,
' external_id ' : item [ ' id ' ]
} ,
' related ' : [ ]
}
# rel to techniques
for technique in item [ ' techniques ' ] :
element [ ' related ' ] . append ( {
' dest-uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , technique ) ) ,
' type ' : ' mitigates '
} )
2024-06-18 14:30:39 +02:00
mitigation_cluster . append ( element , skip_duplicates = True )
2024-06-17 12:21:12 +02:00
2024-06-18 14:30:39 +02:00
mitigation_cluster . save ( ' mitre-fight-mitigations ' )
for cluster , duplicate in mitigation_cluster . duplicates :
print ( f " Skipped duplicate: { duplicate } in cluster { cluster } " )
try :
mitigation_galaxy = Galaxy ( ' mitre-fight-mitigations ' )
2024-06-24 11:05:13 +02:00
except ( KeyError , FileNotFoundError ) :
2024-06-18 14:30:39 +02:00
mitigation_galaxy = Galaxy ( {
' description ' : galaxy_description ,
' icon ' : " shield-alt " ,
' name ' : mitigation_galaxy_name ,
' namespace ' : " mitre " ,
' type ' : galaxy_type ,
' uuid ' : " bcd85ca5-5ed7-4536-bca6-d16fb51adf55 " ,
' version ' : 1
} )
mitigation_galaxy . save ( ' mitre-fight-mitigations ' )
#
2024-06-17 12:21:12 +02:00
# data sources / detections
2024-06-18 14:30:39 +02:00
#
detection_galaxy_name = " MITRE FiGHT Data Sources "
detection_cluster = Cluster ( {
' authors ' : [ " MITRE " ] ,
' category ' : ' data-source ' ,
' name ' : detection_galaxy_name ,
' description ' : galaxy_description ,
' source ' : galaxy_source ,
' type ' : galaxy_type ,
' uuid ' : " fb4410a1-5a39-4b30-934a-9cdfbcd4d2ad " ,
' version ' : 1
} )
2024-06-17 12:21:12 +02:00
for item in fight [ ' data sources ' ] :
element = {
' value ' : item [ ' name ' ] . strip ( ) ,
' description ' : item [ ' description ' ] . strip ( ) ,
' uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , item [ ' id ' ] ) ) ,
' meta ' : {
' kill_chain ' : [ ] ,
' refs ' : [ f " https://fight.mitre.org/data%sources/ { item [ ' id ' ] } " ] ,
' external_id ' : item [ ' id ' ]
} ,
' related ' : [ ]
}
# rel to techniques
for technique in item [ ' techniques ' ] :
element [ ' related ' ] . append ( {
' dest-uuid ' : str ( uuid . uuid5 ( uuid . UUID ( uuid_seed ) , technique ) ) ,
' type ' : ' detects '
} )
2024-06-18 14:30:39 +02:00
detection_cluster . append ( element , skip_duplicates = True )
detection_cluster . save ( ' mitre-fight-datasources ' )
for cluster , duplicate in detection_cluster . duplicates :
print ( f " Skipped duplicate: { duplicate } in cluster { cluster } " )
try :
detection_galaxy = Galaxy ( ' mitre-fight-datasources ' )
2024-06-24 11:05:13 +02:00
except ( KeyError , FileNotFoundError ) :
2024-06-18 14:30:39 +02:00
detection_galaxy = Galaxy ( {
' description ' : galaxy_description ,
' icon ' : " bell " ,
' name ' : detection_galaxy_name ,
' namespace ' : " mitre " ,
' type ' : galaxy_type ,
' uuid ' : " 4ccc2400-55e4-42c2-bb8d-1d41883cef46 " ,
' version ' : 1
} )
detection_galaxy . save ( ' mitre-fight-datasources ' )
2024-06-17 10:01:26 +02:00
print ( " All done, please don ' t forget to ./jq_all_the_things.sh, commit, and then ./validate_all.sh. " )