Path: blob/master/tools/dev/generate_mitre_attack_technique_constants.rb
19508 views
#!/usr/bin/env ruby1# -*- coding: binary -*-23# Pre-requisites:4# Run the following command to fetch the latest MITRE ATT&CK data and add it to a JSON file called mitre_attack.json:5# curl -s 'https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json' | jq '[.objects[] | select(.type == "attack-pattern")]' > mitre_attack.json67# This script generates Ruby constants for MITRE ATT&CK techniques and sub-techniques.8# It reads a JSON file (typically mitre_attack.json) containing MITRE ATT&CK data,9# extracts and formats technique names and IDs, sorts and groups them so that base10# techniques appear before their sub-techniques, and writes the result as a Ruby module.11#12# Usage:13# ruby tools/dev/generate_mitre_attack_technique_constants.rb1415require 'json'16require 'fileutils'1718# Required to handle transliteration of Non-ASCII characters in technique names, example: "T1186_PROCESS_DOPPELGÄNGING"19require 'i18n'20I18n.config.available_locales = :en2122class MitreAttackConstantsGenerator23def initialize(input_file, output_file)24@input_file = input_file25@output_file = output_file26end2728def run29data = read_json30constants = extract_constants(data)31grouped_constants = group_constants(constants)32write_output(grouped_constants)33end3435private3637def read_json38JSON.parse(File.read(@input_file))39end4041def extract_constants(data)42constants = []43data.each do |technique|44next unless technique['type'] == 'attack-pattern'4546description = technique['name']47id_entry = technique['external_references'].select { |hash| hash['external_id'] }48id = id_entry.first['external_id']49const_id = id.gsub('.', '_')50const_description = I18n.transliterate(description).upcase.gsub(/[^A-Z0-9]+/, '_').gsub(/^_+|_+$/, '')51const = "#{const_id}_#{const_description}"52constants << const53end54constants.sort55end5657def group_constants(constants)58constants59.group_by { |const| const[/T\d{4}/] }60.values61.map do |group|62group.sort_by { |const| [const[/T\d{4}_(\d{3})/, 1].to_i] }63end64end6566def write_output(grouped_constants)67output = []68output << '# frozen_string_literal: true'69output << ''70output << 'module Msf'71output << ' module Mitre'72output << ' module Attack'73output << " # This file was auto-generated by #{__FILE__} please do not manually edit it"74output << ' module Technique'75grouped_constants.each_with_index do |group, idx|76group.each do |const|77output << " #{const} = '#{const.match(/T\d{4}(?:_\d{3})?/).to_s.gsub('_', ".")}'"78end79output << '' unless idx == grouped_constants.size - 180end81output << ' end'82output << ' end'83output << ' end'84output << 'end'85output << ''8687FileUtils.mkdir_p(File.dirname(@output_file))88File.write(@output_file, output.join("\n"))89end90end9192# Example usage:93generator = MitreAttackConstantsGenerator.new('mitre_attack.json', 'lib/msf/core/mitre/attack/technique.rb')94generator.run959697