Merge pull request #566 from christophetd/sigma2attack

Add sigma2attack
This commit is contained in:
Thomas Patzke 2019-12-20 21:57:02 +01:00 committed by GitHub
commit 781f53332b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 0 deletions

View File

@ -254,6 +254,27 @@ sigma2misp @misp.conf --same-event --info "Test Event" -r sigma_rules/
[Evt2Sigma](https://github.com/Neo23x0/evt2sigma) helps you with the rule creation. It generates a Sigma rule from a log entry.
## Sigma2attack
Generates a [MITRE ATT&CK Navigator](https://github.com/mitre/attack-navigator/) heatmap from a directory containing sigma rules.
Requirements:
- Sigma rules tagged with a `attack.tXXXX` tag (e.g.: `attack.t1086`)
Usage samples:
```
# Use the default "rules" folder
./tools/sigma2attack
# ... or specify your own
./tools/sigma2attack --rules-directory ~/hunting/rules
```
Result once imported in the MITRE ATT&CK Navigator ([online version](https://mitre-attack.github.io/attack-navigator/enterprise/)):
![Sigma2attack result](./images/sigma2attack.png)
## Contributed Scripts
The directory `contrib` contains scripts that were contributed by the community:

BIN
images/sigma2attack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

69
tools/sigma2attack Executable file
View File

@ -0,0 +1,69 @@
#!/usr/bin/env python3
import argparse
import glob
import json
import os
import sys
import yaml
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--rules-directory", "-d", dest="rules_dir", default="rules", help="Directory to read rules from")
parser.add_argument("--out-file", "-o", dest="out_file", default="heatmap.json", help="File to write the JSON layer to")
parser.add_argument("--no-comment", dest="no_comment", action="store_true", help="Don't store rule names in comments")
args = parser.parse_args()
rule_files = glob.glob(os.path.join(args.rules_dir, "**/*.yml"), recursive=True)
techniques_to_rules = {}
curr_max_technique_count = 0
num_rules_used = 0
for rule_file in rule_files:
try:
rule = yaml.safe_load(open(rule_file).read())
except yaml.YAMLError:
sys.stderr.write("Ignoring rule " + rule_file + " (parsing failed)\n")
continue
if "tags" not in rule:
sys.stderr.write("Ignoring rule " + rule_file + " (no tags)\n")
continue
tags = rule["tags"]
for tag in tags:
if tag.lower().startswith("attack.t"):
technique_id = tag[len("attack."):].upper()
num_rules_used += 1
if technique_id not in techniques_to_rules:
techniques_to_rules[technique_id] = []
techniques_to_rules[technique_id].append(os.path.basename(rule_file))
curr_max_technique_count = max(curr_max_technique_count, len(techniques_to_rules[technique_id]))
scores = []
for technique in techniques_to_rules:
entry = {
"techniqueID": technique,
"score": len(techniques_to_rules[technique]),
}
if not args.no_comment:
entry["comment"] = "\n".join(techniques_to_rules[technique])
scores.append(entry)
output = {
"domain": "mitre-enterprise",
"name": "Sigma rules heatmap",
"gradient": {
"colors": [
"#ffffff",
"#ff6666"
],
"maxValue": curr_max_technique_count,
"minValue": 0
},
"version": "2.2",
"techniques": scores,
}
with open(args.out_file, "w") as f:
f.write(json.dumps(output))
print("[*] Layer file written in " + args.out_file + " (" + str(num_rules_used) + " rules)")