mirror of
https://github.com/valitydev/SigmaHQ.git
synced 2024-11-07 09:48:58 +00:00
Merge pull request #566 from christophetd/sigma2attack
Add sigma2attack
This commit is contained in:
commit
781f53332b
21
README.md
21
README.md
@ -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
BIN
images/sigma2attack.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 344 KiB |
69
tools/sigma2attack
Executable file
69
tools/sigma2attack
Executable 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)")
|
Loading…
Reference in New Issue
Block a user