mirror of
https://github.com/MBCProject/mbc-markdown
synced 2026-06-08 11:36:36 +00:00
Merge pull request #76 from ryantxu1/master
Removing zscript from master branch
This commit is contained in:
@@ -37,7 +37,7 @@ See ATT&CK: [Netwalker - Techniques Used](https://attack.mitre.org/software/S045
|
||||
|---|---|
|
||||
|[Execution::Command and Scripting Interpreter (E1049)](../execution/command-and-scripting-interpreter.md)|Netwalker is written and executed in Powershell [[1]](#1)|
|
||||
|[Defense Evasion::Obfuscated Files or Information (E1027)](../defense-evasion/obfuscated-files-or-information.md)|Netwalker is obfuscated with several layers of encoding, obfuscation, and encryption techniques such as base64, hexademcimal, and XOR [[1]](#1)|
|
||||
|[Defense Evasion::Process Injection::Dynamic-Link Library Injection (E1055.001)](../defense-evasion/process-injection.md)|Netwalker uses reflective DLL loading to inject from memory [[1]](#1)|
|
||||
|[Defense Evasion::Process Injection::Dynamic-link Library Injection (E1055.001)](../defense-evasion/process-injection.md)|Netwalker uses reflective DLL loading to inject from memory [[1]](#1)|
|
||||
|[Impact::Data Encrypted for Impact (E1486)](../impact/data-encrypted-for-impact.md)|Netwalker encrypts files for ransom [[1]](#1)|
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
- Update capa_analysis.py to make it look pretty and increase usability
|
||||
- update autofill.py to make it look pretty and increase usability
|
||||
- Move global variables such as objective_list into a config file to reduce editing if things are changed
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,34 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
# Converts all header format to atx (# ...)
|
||||
def dir_walk(directory):
|
||||
ignorelist = ["nursery", '.github', '.git', 'LICENSE.txt', 'README.md', '.gitattributes', 'ynewsletters', 'yfaq', 'zscript']
|
||||
|
||||
# Iterate recursively over all files in the repo
|
||||
for root, dirs, files in os.walk(directory):
|
||||
if not any(block in root for block in ignorelist):
|
||||
for name in files:
|
||||
f = open(os.path.join(root, name), "r")
|
||||
lines = f.readlines() # Read contents of each capa rule
|
||||
f.close()
|
||||
|
||||
for line in lines:
|
||||
equals = re.search("={3,}\n", line) # Search capa rule for attack mapping, if found, add to attack counter
|
||||
|
||||
if equals:
|
||||
print(os.path.join(root, name))
|
||||
index = lines.index(equals.group(0))
|
||||
lines.remove(equals.group(0))
|
||||
lines[index-1] = "# {0}\n".format(lines[index-1])
|
||||
|
||||
|
||||
with open(os.path.join(root, name), 'w') as f:
|
||||
for line in lines:
|
||||
f.write(line)
|
||||
f.close()
|
||||
|
||||
|
||||
|
||||
dir_walk('..')
|
||||
@@ -1,277 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import re
|
||||
import glob
|
||||
|
||||
from fuzzywuzzy import fuzz # pip install fuzzywuzzy python-Levenshtein
|
||||
|
||||
|
||||
MALWARE_EXAMPLES_HEADER = "\nMalware Examples\n----------------\n|Name|Date|Description|\n|---|---|---|\n"
|
||||
REFERENCES_HEADER = "\nReferences\n----------"
|
||||
ATTACK_TECHNIQUE_HEADER = "\nATT&CK Techniques\n-----------------\n|Name|Use|\n|---|---|\n"
|
||||
BEHAVIORS_HEADER = "\nBehaviors\n---------\n|Name|Use|\n|---|---|\n"
|
||||
|
||||
def file_search(file_lines, string): # Searches file f for a string, returns the line number. If not found, return -1
|
||||
count = 0
|
||||
|
||||
for x in file_lines:
|
||||
if string in x:
|
||||
return count
|
||||
|
||||
count += 1
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
def fuzzy_file_directory_search(files, string): # Searches file directories for a file using fuzzy matching
|
||||
count = 0
|
||||
f = ''
|
||||
for file in files:
|
||||
x = fuzz.partial_ratio(file, string)
|
||||
# print(file + " " + str(x))
|
||||
if x > count: # Returns highest fuzzy match score
|
||||
count = x
|
||||
f = file
|
||||
|
||||
if count == 0 or f is None:
|
||||
raise Exception("Fuzzy file search did not find anything: ".format(string))
|
||||
|
||||
return f
|
||||
|
||||
|
||||
def populate_behavior(name, date, reference, path, description, orig_file):
|
||||
# if len(sys.argv) != 4:
|
||||
# print("This script needs three command-and-scripting-interpreter parameters: the malware name, date, and reference link. Additionally, paste the markdown into the input file")
|
||||
# return
|
||||
|
||||
|
||||
# malware_name = sys.argv[1]
|
||||
# malware_date = sys.argv[2]
|
||||
# reference_link = sys.argv[3]
|
||||
# input_regex = "\[[^]]*\]\(([^\)]*)\)\|(.*?) \["
|
||||
# input_regex2 = "\[[^]]*\]\(([^\)]*)\)\|"
|
||||
reference_regex = "<a name="
|
||||
|
||||
# lines = []
|
||||
# with open("input") as f: # Reading input file
|
||||
# lines = f.readlines()
|
||||
|
||||
# for line in lines: # Pulling path+description from input file
|
||||
# x = re.search(input_regex, line)
|
||||
# try:
|
||||
# path = x.group(1)
|
||||
# description = x.group(2)
|
||||
# except AttributeError:
|
||||
# x = re.search(input_regex2, line)
|
||||
# path = x.group(1)
|
||||
# description = ""
|
||||
|
||||
with open(path, "r+") as f: # Navigating to 'path', determining if behavior already has a 'Malware Example' section
|
||||
mbcBehaviorLines = f.readlines()
|
||||
f.seek(0)
|
||||
mbcFile = f.read()
|
||||
|
||||
if file_search(mbcBehaviorLines, name) != -1: # If malware is already listed, skip this file
|
||||
print("[+] Detecting that {} is already in {}\n".format(name, path))
|
||||
return
|
||||
|
||||
malware_examples_line = file_search(mbcBehaviorLines, 'Malware Examples') # Determining if Malware Examples and Reference sections are present
|
||||
references_line = file_search (mbcBehaviorLines, 'References')
|
||||
|
||||
if malware_examples_line == -1: # If there is no malware_examples line, build one
|
||||
if references_line == -1: # If there is no reference section, we can write malware_examples to the end
|
||||
mbcBehaviorLines.append(MALWARE_EXAMPLES_HEADER)
|
||||
malware_examples_line = len(mbcBehaviorLines) - 1
|
||||
else:
|
||||
mbcBehaviorLines.insert(references_line - 1, MALWARE_EXAMPLES_HEADER)
|
||||
malware_examples_line = references_line - 1
|
||||
|
||||
if references_line == -1: # If there is no reference section, write a reference header at the end of the file
|
||||
mbcBehaviorLines.append(REFERENCES_HEADER)
|
||||
references_line = len(mbcBehaviorLines)
|
||||
|
||||
# Count number of references present, then add one for the new entry
|
||||
reference_num = len(re.findall(reference_regex, mbcFile)) + 1
|
||||
|
||||
# Building the text
|
||||
malware_example_text = "|[**{}**]({})|{}|{} [[{}]](#{})|\n".format(name, orig_file, date, description, reference_num, reference_num)
|
||||
reference_text = '\n<a name="{}">[{}]</a> {}\n'.format(reference_num, reference_num, reference)
|
||||
|
||||
# Insert malware notation in the line above the Reference line (which should be the bottom of malware examples)
|
||||
# Insert new reference at the end
|
||||
mbcBehaviorLines.insert(references_line - 1, malware_example_text)
|
||||
mbcBehaviorLines.append(reference_text)
|
||||
|
||||
# Fix some spacing issues between sections if present
|
||||
malware_examples_line = file_search(mbcBehaviorLines, 'Malware Examples')
|
||||
if mbcBehaviorLines[malware_examples_line - 1] != '\n':
|
||||
mbcBehaviorLines.insert(malware_examples_line, '\n')
|
||||
|
||||
references_line = file_search(mbcBehaviorLines, 'References')
|
||||
if mbcBehaviorLines[references_line - 1] != '\n':
|
||||
mbcBehaviorLines.insert(references_line, '\n')
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.writelines(mbcBehaviorLines)
|
||||
|
||||
print("[+] Inserting Malware Reference into {}\n".format(path))
|
||||
return
|
||||
|
||||
|
||||
# Receives an input file such as 'example_input', parses the file and inserts corresponding information into the markdown
|
||||
# NOTE: Does require the example malware file to be created first
|
||||
def populate_malware_example(input_file):
|
||||
references = []
|
||||
input_regex = "(.*?) (\(\S+)\s+\[(\d)\]\s(.*)"
|
||||
input_regex2 = "(.*?) (\(\S+)\s+\[(\d)\]\s"
|
||||
reference_regex = '<a name="\d+">\[\d+\]<\/a> (\S+)'
|
||||
date_regex = "\|\*\*Year\*\*\|(.*?)\|"
|
||||
|
||||
mbc_file_map = glob.glob('../**/*.md', recursive=True) # Get list of all files in MBC
|
||||
|
||||
with open(input_file) as f: # Reading input file
|
||||
# Read first line, which should be a name
|
||||
line = f.readline()
|
||||
if not re.search('X\d{4}', line): # Asserts that name line (Kraken X0010) is correctly formatted
|
||||
raise Exception("Name syntax not correct")
|
||||
name_line = line.split()
|
||||
|
||||
# Start iteratively reading the next lines
|
||||
input_lines = f.readlines()
|
||||
|
||||
print("[+] Input File parsed, searching for {}".format(name_line[0]))
|
||||
|
||||
# Try to match malware name to the corresponding xample-malware using fuzzy string matching
|
||||
malware_files = glob.glob("../xample-malware/*")
|
||||
name = "../xample-malware/" + name_line[0].lower() + ".md"
|
||||
modified_xample_file = fuzzy_file_directory_search(malware_files, name)
|
||||
|
||||
print("[+] Malware file found: {}\n".format(modified_xample_file))
|
||||
|
||||
|
||||
# Reading the xample-malware file
|
||||
with open(modified_xample_file) as f:
|
||||
xample_file_lines = f.readlines()
|
||||
f.seek(0)
|
||||
xample_file_whole = f.read()
|
||||
|
||||
|
||||
# Verify file id with xample_file
|
||||
if name_line[1] not in xample_file_whole:
|
||||
raise("Exception malware ID did not match file")
|
||||
|
||||
# Get date
|
||||
date = re.findall(date_regex, xample_file_whole)[0]
|
||||
|
||||
# Searching xample-malware file to see what sections are present
|
||||
attack_techniques_line = file_search(xample_file_lines, 'ATT&CK Techniques')
|
||||
behaviors_line = file_search(xample_file_lines, 'Behaviors')
|
||||
references_line = file_search(xample_file_lines, 'References')
|
||||
|
||||
# Fill in missing sections
|
||||
if references_line == -1:
|
||||
xample_file_lines.append(REFERENCES_HEADER)
|
||||
# print(xample_file_lines)
|
||||
|
||||
if behaviors_line == -1:
|
||||
references_line = file_search(xample_file_lines, 'References')
|
||||
xample_file_lines.insert(references_line, BEHAVIORS_HEADER)
|
||||
|
||||
if attack_techniques_line == -1:
|
||||
behaviors_line = file_search(xample_file_lines, 'Behaviors')
|
||||
xample_file_lines.insert(behaviors_line, ATTACK_TECHNIQUE_HEADER)
|
||||
|
||||
|
||||
# Count number of references present
|
||||
existing_references = re.findall(reference_regex, xample_file_whole)
|
||||
for ref in existing_references:
|
||||
references.append(ref)
|
||||
reference_diff = len(existing_references)
|
||||
|
||||
# Iterate through input file and populate xample-malware file
|
||||
for line in input_lines:
|
||||
if line.isspace():
|
||||
continue
|
||||
|
||||
if "Reference" in line: # Determines if reference is already present, if not, add it
|
||||
reference_link = line.split()[-1]
|
||||
if reference_link not in xample_file_whole:
|
||||
references.append(reference_link)
|
||||
reference_num = len(references)
|
||||
reference_text = '\n<a name="{}">[{}]</a> {}\n'.format(reference_num, reference_num, reference_link)
|
||||
xample_file_lines.append(reference_text)
|
||||
|
||||
else: # Parse behavior/technique lines
|
||||
print("[+] Parsing " + line.replace("\n", ''))
|
||||
x = re.search(input_regex, line)
|
||||
try:
|
||||
technique_behavior = x.group(1)
|
||||
id = x.group(2)
|
||||
reference = x.group(3)
|
||||
description = x.group(4)
|
||||
except AttributeError: # In case description is absent
|
||||
x = re.search(input_regex2, line)
|
||||
technique_behavior = x.group(1)
|
||||
id = x.group(2)
|
||||
reference = x.group(3)
|
||||
|
||||
if reference_diff > 0:
|
||||
reference_diff -= 1
|
||||
reference = str(int(reference) + reference_diff)
|
||||
reference_link = references[int(reference) - 1]
|
||||
|
||||
if id in xample_file_whole:
|
||||
print("[+] Skipping {} because already present".format(id))
|
||||
continue
|
||||
|
||||
|
||||
if 'T' in id: # Determine if ATT&CK technique
|
||||
attack_link = "https://attack.mitre.org/techniques/{}/".format(id.replace('(', '').replace(')', '').replace('.', '/'))
|
||||
technique_text = "|[{} {}]({})|{} [[{}]](#{})|\n".format(technique_behavior, id, attack_link, description, reference, reference)
|
||||
|
||||
behaviors_line = file_search(xample_file_lines, 'Behaviors')
|
||||
xample_file_lines.insert(behaviors_line, technique_text)
|
||||
|
||||
print("[+] Inserting ATT&CK technique: {}\n".format(technique_behavior))
|
||||
|
||||
else: # If it is an MBC behavior
|
||||
# Try to fuzzy match the input behavior to a file
|
||||
print("[+] Searching for {}".format(technique_behavior))
|
||||
mbc_file = fuzzy_file_directory_search(mbc_file_map, technique_behavior)
|
||||
print("[+] Behavior found: {}".format(mbc_file))
|
||||
|
||||
behavior_text = "|[{} {}]({})|{} [[{}]](#{})|\n".format(technique_behavior, id, mbc_file, description, reference, reference)
|
||||
|
||||
references_line = file_search(xample_file_lines, 'References')
|
||||
xample_file_lines.insert(references_line, behavior_text)
|
||||
|
||||
# Add malware entry into the behavior file itself
|
||||
populate_behavior(name_line[0], date, reference_link, mbc_file, description, modified_xample_file)
|
||||
|
||||
# Fix some spacing issues between sections if present
|
||||
behaviors_line = file_search(xample_file_lines, 'Behaviors')
|
||||
if xample_file_lines[behaviors_line - 1] != '\n':
|
||||
xample_file_lines.insert(behaviors_line, '\n')
|
||||
|
||||
references_line = file_search(xample_file_lines, 'References')
|
||||
if xample_file_lines[references_line - 1] != '\n':
|
||||
xample_file_lines.insert(references_line, '\n')
|
||||
|
||||
|
||||
with open(modified_xample_file, "w") as f:
|
||||
f.writelines(xample_file_lines)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("This script needs one command-and-scripting-interpreter parameters: the path to the input file")
|
||||
return
|
||||
|
||||
|
||||
input_file = sys.argv[1]
|
||||
populate_malware_example(input_file)
|
||||
return
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
main()
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
import termplotlib as tpl
|
||||
import yaml
|
||||
|
||||
|
||||
# Walk through capa directory, ignoring files within the ignorelist folders
|
||||
# Read each file and use regex to match mbc or attack mappings
|
||||
# If found, insert them into the corresponding Counter
|
||||
def dir_walk(directory):
|
||||
attack_regex = "att&ck:\s*- ([^\n]*)"
|
||||
mbc_regex = "mbc:\s*- ([^\n]*)"
|
||||
ignorelist = ["nursery", '.github', 'LICENSE.txt', 'README.md', '.gitattributes', '.git']
|
||||
|
||||
attack_mappings = Counter()
|
||||
mbc_mappings = Counter()
|
||||
|
||||
# Iterate recursively over all files in the repo
|
||||
for root, dirs, files in os.walk(directory):
|
||||
if not any(block in root for block in ignorelist):
|
||||
for name in files:
|
||||
f = open(os.path.join(root, name), "r")
|
||||
capa_rule = f.read() # Read contents of each capa rule
|
||||
|
||||
attack = re.search(attack_regex, capa_rule) # Search capa rule for attack mapping, if found, add to attack counter
|
||||
if attack:
|
||||
attack_mappings.update(Counter([attack.group(1)]))
|
||||
|
||||
mbc = re.search(mbc_regex, capa_rule)
|
||||
if mbc:
|
||||
mbc_mappings.update(Counter([mbc.group(1)]))
|
||||
|
||||
return attack_mappings, mbc_mappings
|
||||
|
||||
def plot(dict):
|
||||
keys = list(dict.keys())
|
||||
nums = [dict[x]['num'] for x in keys]
|
||||
fig = tpl.figure()
|
||||
fig.barh(nums, keys, force_ascii=True)
|
||||
fig.show()
|
||||
|
||||
# Takes a list of tactics/behaviors and converts them as keys in a dict
|
||||
# Iterates over list of mappings and split the strings to separate tactics/techniques, behaviors/methods
|
||||
# Insert into dictionary
|
||||
# Print out results and create a histogram
|
||||
def analysis(map, keys, mbc_micro_behaviors=None):
|
||||
tactic_behavior_dict = {k: {'num':0, 'rule':[]} for k in keys}
|
||||
if mbc_micro_behaviors:
|
||||
micro_behavior_dict = {k: {'num':0, 'rule':[]} for k in mbc_micro_behaviors}
|
||||
|
||||
for mapping in map:
|
||||
try:
|
||||
split_map = mapping.split("::", 1)
|
||||
tactic_behavior = split_map[0]
|
||||
tactic_behavior_dict[tactic_behavior]['rule'].append(split_map[1])
|
||||
tactic_behavior_dict[tactic_behavior]['num'] += 1
|
||||
except KeyError:
|
||||
if mbc_micro_behaviors:
|
||||
try:
|
||||
micro_behavior_dict[tactic_behavior]['rule'].append(split_map[1])
|
||||
micro_behavior_dict[tactic_behavior]['num'] += 1
|
||||
except KeyError:
|
||||
print("The following MBC mapping could not be identified: " + str(mapping))
|
||||
else:
|
||||
print("The following ATT&CK mapping could not be identified: " + str(mapping))
|
||||
|
||||
|
||||
|
||||
|
||||
if mbc_micro_behaviors is None:
|
||||
print("---------ATT&CK MAPPINGS---------")
|
||||
print(yaml.dump(tactic_behavior_dict, default_flow_style=False))
|
||||
plot(tactic_behavior_dict)
|
||||
else:
|
||||
print("\n---------MBC MAPPINGS---------")
|
||||
print(yaml.dump(tactic_behavior_dict, default_flow_style=False))
|
||||
plot(tactic_behavior_dict)
|
||||
print("\n---------MBC MICRO-BEHAVIOR MAPPINGS---------")
|
||||
print(yaml.dump(micro_behavior_dict, default_flow_style=False))
|
||||
plot(micro_behavior_dict)
|
||||
|
||||
def return_mbc_mapping(capa_dir):
|
||||
attack_mapping, mbc_mapping = dir_walk(capa_dir)
|
||||
return dict(mbc_mapping)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.argv[1] == "-h":
|
||||
print("This script analyzes the capa repo to determine the coverage wrt ATT&CK + MBC. \n Usage: python3 capa_analysis.py <directory>")
|
||||
directory = sys.argv[1]
|
||||
|
||||
attack_tactics = ["Reconnaissance", "Resource Development", "Initial Access", "Execution", "Persistence", "Privilege Escalation", "Defense Evasion", "Credential Access", "Discovery", "Lateral Movement", "Collection", "Command and Control", "Exfiltration", "Impact"]
|
||||
mbc_behaviors = ["Anti-Behavioral Analysis", "Anti-Static Analysis", "Collection", "Command and Control", "Credential Access", "Defense Evasion", "Discovery", "Execution", "Exfiltration", "Impact", "Lateral Movement","Persistence", "Privilege Escalation"]
|
||||
mbc_micro_behaviors = ["Communication", "Cryptography", "Data", "File System", "Hardware", "Memory", "Operating System", "Process"]
|
||||
|
||||
|
||||
attack_mapping, mbc_mapping = dir_walk(directory)
|
||||
analysis(attack_mapping, attack_tactics)
|
||||
|
||||
analysis(mbc_mapping, mbc_behaviors, mbc_micro_behaviors=mbc_micro_behaviors)
|
||||
|
||||
print(dict(mbc_mapping))
|
||||
@@ -1,57 +0,0 @@
|
||||
"""
|
||||
Analyze CAPEv2 community signature modules, extract information, and produce
|
||||
CSV content.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import importlib
|
||||
import inspect
|
||||
import logging
|
||||
import logging.config
|
||||
import pathlib
|
||||
import stix2
|
||||
import stix2.utils
|
||||
import sys
|
||||
|
||||
# From CAPEv2, or the stubs
|
||||
import lib.cuckoo.common.abstracts
|
||||
|
||||
# From CAPEv2 community repo; successfully importing this module probably
|
||||
# requires adding the appropriate path to $PYTHONPATH.
|
||||
#
|
||||
# See: https://github.com/kevoreilly/community
|
||||
import modules.signatures
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""
|
||||
Parse commandline arguments.
|
||||
"""
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
description="Analyze CAPEv2 community signatures and create CSV"
|
||||
" content."
|
||||
)
|
||||
|
||||
|
||||
arg_parser.add_argument(
|
||||
"-o", "--out", required=True,
|
||||
help="""
|
||||
Directory path to the CAPE community repo.
|
||||
"""
|
||||
)
|
||||
|
||||
arg_parser.add_argument(
|
||||
"-o", "--out", required=True
|
||||
help="""
|
||||
A filename to write content to. If not given, write to stdout.
|
||||
"""
|
||||
)
|
||||
|
||||
return arg_parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,10 +0,0 @@
|
||||
Kraken X0010
|
||||
Date 2008
|
||||
Reference [1] http://blog.threatexpert.com/2008/04/kraken-changes-tactics.html
|
||||
|
||||
|
||||
Command and Control::Dynamic Resolution:Domain Generation Algorithms (T1568.002) [1] Uses a domain name generator.
|
||||
Command and Control::Application Layer Protocol::Web Protocols (T1568.002) [1] The malware uses HTTP to communicate with C2
|
||||
|
||||
Command and Control::Domain Name Generation (B0003) [1]
|
||||
Anti-Behavioral Analysis::Memory Dump Evasion (B0006) [1] Dumping Kraken's c.dll module from the heap of its own process is tricky because its PE-header is wiped out.
|
||||
@@ -1,215 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
from capa_analysis import return_mbc_mapping
|
||||
|
||||
objective_list = ['anti-behavioral-analysis', 'anti-static-analysis', 'collection', 'command-and-control', 'credential-access', 'defense-evasion', 'discovery', 'execution', 'exfiltration', 'impact', 'lateral-movement', 'micro-behaviors', 'persistence', 'privilege-escalation']
|
||||
capa_rule_blacklist = ['README', 'format']
|
||||
|
||||
def yaml_find(d, tag):
|
||||
if tag in d:
|
||||
yield d[tag]
|
||||
for k, v in d.items():
|
||||
if isinstance(v, dict):
|
||||
for i in yaml_find(v, tag):
|
||||
yield i
|
||||
|
||||
|
||||
|
||||
# Fills file with the detection capa + CAPE headers. Returns new lines and indexes of capa and cape indicating where new entries should be placed
|
||||
def fill_detection(behavior_lines, index):
|
||||
behavior_lines.insert(index, "\n")
|
||||
behavior_lines.insert(index+1, "## Detection\n")
|
||||
behavior_lines.insert(index+2, "\n")
|
||||
behavior_lines.insert(index+3, "|Tool: capa|Mapping|APIs|\n")
|
||||
behavior_lines.insert(index+4, "|---|---|---|\n")
|
||||
behavior_lines.insert(index+5, "\n")
|
||||
behavior_lines.insert(index+6, "|Tool: CAPE|Mapping|APIs|\n")
|
||||
behavior_lines.insert(index+7, "|---|---|---|\n")
|
||||
behavior_lines.insert(index+8, "\n")
|
||||
|
||||
return behavior_lines, index+5, index+8
|
||||
|
||||
|
||||
# Starting at 'index', parse each line for text in between '[]', which is the rule name. Ends when a blank line is reached. Returns array of rules
|
||||
def detect_existing_rules(behavior_lines, index):
|
||||
rules = []
|
||||
rule_line = behavior_lines[index]
|
||||
rule_re = "\[([^\]]+)]"
|
||||
|
||||
while capa_line != "\n":
|
||||
rules.append(re.search(rule_re, rule_line).group(1))
|
||||
|
||||
index += 1
|
||||
capa_line = behavior_lines[index]
|
||||
|
||||
return rules
|
||||
|
||||
# Searches capa-rules repo for mentions of id using subprocess + grep, returns list of capa files that have MBC rule
|
||||
def search_capa_rules(id, capa_repo):
|
||||
capa_rule_paths = []
|
||||
grep_output = subprocess.run(["grep", "-r", id, capa_repo], capture_output=True)
|
||||
grep_output = grep_output.stdout
|
||||
|
||||
rules = grep_output.decode().split('\n')
|
||||
|
||||
for rule in rules:
|
||||
if rule == '':
|
||||
continue
|
||||
|
||||
rule_path = rule.split(' ', 1)[0][:-1]
|
||||
|
||||
if not any(substring in rule_path for substring in capa_rule_blacklist):
|
||||
capa_rule_paths.append(rule_path)
|
||||
|
||||
return capa_rule_paths
|
||||
|
||||
|
||||
# Parses given capa rules and generates and inserts text into MBC file
|
||||
def fill_capa_rules(capa_rules_path, behavior_lines):
|
||||
for rule in capa_rules_path:
|
||||
with open(rule) as f:
|
||||
capa_yaml = yaml.safe_load(f)
|
||||
f.close()
|
||||
|
||||
# print(capa_yaml)
|
||||
rule_name = capa_yaml['rule']['meta']['name']
|
||||
api = list(yaml_find(capa_yaml, 'api'))
|
||||
|
||||
if len(api) > 1:
|
||||
raise Exception(">1 API FOUND")
|
||||
|
||||
api = api[0]
|
||||
|
||||
|
||||
|
||||
print(capa_yaml.keys())
|
||||
print(rule_name)
|
||||
print(api)
|
||||
|
||||
|
||||
|
||||
raise Exception
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def test(file):
|
||||
try:
|
||||
with open(file) as f:
|
||||
behavior_lines = f.readlines()
|
||||
f.close()
|
||||
except FileNotFoundError:
|
||||
print("[X] FILE NOT FOUND: " + file)
|
||||
return -1
|
||||
|
||||
# Determine if 'Detection' section already present
|
||||
if "## Detection\n" not in behavior_lines:
|
||||
if "## Code Snippets\n" in behavior_lines:
|
||||
index = behavior_lines.index("## Code Snippets\n") - 1
|
||||
elif "## References\n" in behavior_lines:
|
||||
index = behavior_lines.index("## References\n") - 1
|
||||
else:
|
||||
print("[X] Code Snippet or Reference Section not found")
|
||||
return -1
|
||||
|
||||
behavior_lines, capa_index, cape_index = fill_detection(behavior_lines, index)
|
||||
|
||||
|
||||
else:
|
||||
capa_index = behavior_lines.index("|Tool: capa|Mapping|APIs|\n") + 2
|
||||
cape_index = behavior_lines.index("|Tool: CAPE|Mapping|APIs|\n") + 2
|
||||
|
||||
# If 'Detection' section present, detect existing rules
|
||||
capa_rules = detect_existing_rules(behavior_lines, capa_index)
|
||||
cape_rules = detect_existing_rules(behavior_lines, cape_index)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
with open(file, 'w') as f:
|
||||
for line in behavior_lines:
|
||||
f.write(line)
|
||||
f.close()
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize parser
|
||||
parser = argparse.ArgumentParser(description="Inserts a 'detection' section into behaviors. The section will contain info on capa and cape rules related to that behavior")
|
||||
|
||||
# Adding arguments
|
||||
parser.add_argument("-i", "--id", help = "ID (B0001)")
|
||||
parser.add_argument("-o", "--objective", nargs='+', help = "Objectives separated by spaces, replace spaces within the name with underscore (Anti-Behavioral_Analysis Anti-Static_Analysis)")
|
||||
parser.add_argument("-a", "--attack", nargs='+', help = "Related ATT&CK Techniques grouped by name and ID (Debugger_Evasion,T1622 Virtualization/Standbox_Evasion_Checks,T1497.001,T1633.001)")
|
||||
parser.add_argument("-t", "--type", help = "Anti-Analysis or Impact Types (Evasion, Detection, Integrity, Breach, Availability)")
|
||||
parser.add_argument("-v", "--version", help="Version")
|
||||
|
||||
parser.add_argument("-m", "--last_modified", help="Last modified date (1_August_2019)")
|
||||
|
||||
parser.add_argument("-c", "--capa", help="capa-rules directory", required=True)
|
||||
parser.add_argument("-f", "--file")
|
||||
|
||||
# Read arguments from command line
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.file:
|
||||
test(args.file)
|
||||
return
|
||||
|
||||
# Gets a dict of all behaviors that are mentioned in capa, along with count
|
||||
capa_behaviors = return_mbc_mapping(args.capa)
|
||||
|
||||
for behavior, count in capa_behaviors.items():
|
||||
# Splitting dict entry into behavior and id
|
||||
# EX: communication/socket-communication/create-udp-socket, C0001.010
|
||||
str_split = behavior.split(" [", 1)
|
||||
behavior_path = str_split[0].lower().replace('::', '/').replace(' ', '-')
|
||||
id = str_split[1][:-1]
|
||||
|
||||
# if method or sub-microbehavior, remove portion from path
|
||||
if '.' in id:
|
||||
str_split = behavior_path.split("/")[:-1]
|
||||
behavior_path = '/'.join(str_split)
|
||||
|
||||
# If microbehavior, add 'micro-behaviors' to behavior path
|
||||
objective = behavior_path.split('/')[0]
|
||||
if objective not in objective_list:
|
||||
behavior_path = 'micro-behaviors/' + behavior_path
|
||||
|
||||
behavior_path = '../' + behavior_path + '.md'
|
||||
try:
|
||||
with open(behavior_path) as f:
|
||||
behavior_lines = f.readlines()
|
||||
f.close()
|
||||
except FileNotFoundError:
|
||||
print("[X] FILE NOT FOUND: " + behavior_path)
|
||||
return -1
|
||||
|
||||
if "## Code Snippets\n" in behavior_lines:
|
||||
index = behavior_lines.index("## Code Snippets\n") - 1
|
||||
elif "## References\n" in behavior_lines:
|
||||
index = behavior_lines.index("## References\n") - 1
|
||||
else: # If Code Snippet or Reference Section missing, return index of last line
|
||||
index = len(behavior_lines) - 1
|
||||
|
||||
x = search_capa_rules(id, args.capa)
|
||||
|
||||
fill_capa_rules(x, behavior_lines)
|
||||
|
||||
|
||||
|
||||
|
||||
# print(capa_behaviors.keys())
|
||||
# print(yaml.dump(capa_behaviors, default_flow_style=False))
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
main()
|
||||
@@ -1,176 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import argparse
|
||||
|
||||
from fuzzywuzzy import fuzz # pip install fuzzywuzzy python-Levenshtein
|
||||
|
||||
objective_list = ['anti-behavioral-analysis', 'anti-static-analysis', 'collection', 'command-and-control', 'credential-access', 'defense-evasion', 'discovery', 'execution', 'exfiltration', 'impact', 'lateral-movement', 'micro-behaviors', 'persistence', 'privilege-escalation']
|
||||
anti_analysis_types = ['Evasion', 'Detection']
|
||||
impact_types = ['Integrity', 'Breach', 'Availability']
|
||||
|
||||
# Checks if 'string' in 'l' at index 'line_num'. If missing, insert 'string' into 'l' at index 'line_num'
|
||||
# Return possibly modified 'l'
|
||||
def insert_if_missing(l, line_num, string):
|
||||
if l[line_num] != string:
|
||||
l.insert(line_num, string)
|
||||
|
||||
return l
|
||||
|
||||
# Matches string to an entry in objective_list
|
||||
def objective_search(string):
|
||||
string = string.lower().replace('_', '-')
|
||||
|
||||
if string not in objective_list:
|
||||
raise Exception("Objective {} did not match anything: ".format(string))
|
||||
|
||||
return "../" + string
|
||||
|
||||
def main():
|
||||
# Initialize parser
|
||||
parser = argparse.ArgumentParser(description="Inserts a formatted header for MBC. Only add parameters that are missing. In case of bugs, only run on a second copy")
|
||||
|
||||
# Adding arguments
|
||||
parser.add_argument("-f", "--file", help = "File to modify", required=True)
|
||||
parser.add_argument("-i", "--id", help = "ID (B0001)")
|
||||
parser.add_argument("-o", "--objective", nargs='+', help = "Objectives separated by spaces, replace spaces within the name with underscore (Anti-Behavioral_Analysis Anti-Static_Analysis)")
|
||||
parser.add_argument("-a", "--attack", nargs='+', help = "Related ATT&CK Techniques grouped by name and ID (Debugger_Evasion,T1622 Virtualization/Standbox_Evasion_Checks,T1497.001,T1633.001)")
|
||||
parser.add_argument("-t", "--type", help = "Anti-Analysis or Impact Types (Evasion, Detection, Integrity, Breach, Availability)")
|
||||
parser.add_argument("-v", "--version", help="Version")
|
||||
parser.add_argument("-c", "--created", help="Method Creation Date (1_August_2019)")
|
||||
parser.add_argument("-m", "--last_modified", help="Last modified date (1_August_2019)")
|
||||
|
||||
# Read arguments from command line
|
||||
args = parser.parse_args()
|
||||
|
||||
# Read file
|
||||
file = args.file
|
||||
with open(file) as f:
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
|
||||
offset = 0 # Handles unique fields such as anti-analysis/impact type
|
||||
|
||||
# Some files have an empty line 1, delete that
|
||||
if lines[0] == '\n':
|
||||
lines.pop(0)
|
||||
|
||||
# Checking if first line is <table>, if not, that means this file does not have any appropriate header formatting
|
||||
lines = insert_if_missing(lines, 0, "<table>\n")
|
||||
lines = insert_if_missing(lines, 1, "<tr>\n")
|
||||
|
||||
# Adding ID field
|
||||
if args.id:
|
||||
lines = insert_if_missing(lines, 2, "<td><b>ID</b></td>\n")
|
||||
lines.insert(3, "<td><b>{}</b></td>\n".format(args.id))
|
||||
lines.insert(4, "</tr>\n")
|
||||
|
||||
# Adding Objective field
|
||||
if args.objective:
|
||||
lines = insert_if_missing(lines, 5, "<tr>\n")
|
||||
lines = insert_if_missing(lines, 6, "<td><b>Objective(s)</b></td>\n")
|
||||
|
||||
# Build objectives if multiple
|
||||
obj_str = "<td><b>"
|
||||
for objective in args.objective:
|
||||
obj_path = objective_search(objective)
|
||||
objective = objective.replace('_', ' ')
|
||||
|
||||
obj_str = obj_str + "<a href=\"{}\">{}</a>, ".format(obj_path, objective)
|
||||
|
||||
# Chop off last ', '
|
||||
obj_str = obj_str[:len(obj_str) - 2]
|
||||
obj_str = obj_str + "</b></td>\n"
|
||||
|
||||
lines.insert(7, obj_str)
|
||||
lines.insert(8, "</tr>\n")
|
||||
|
||||
# Adding Related ATT&CK Techniques Field
|
||||
if args.attack:
|
||||
lines = insert_if_missing(lines, 9, "<tr>\n")
|
||||
lines = insert_if_missing(lines, 10, "<td><b>Related ATT&CK Techniques</b></td>\n")
|
||||
|
||||
# Build attack links if multiple
|
||||
attack_str = "<td><b>"
|
||||
for attack in args.attack:
|
||||
attack = attack.split(",")
|
||||
attack_name = attack[0].replace('_', ' ')
|
||||
|
||||
attack_str = attack_str + attack_name + " ("
|
||||
|
||||
# Looking at technique IDs now
|
||||
for i in range(1, len(attack)):
|
||||
attack_id = attack[i]
|
||||
attack_id_dash = attack_id.replace('.', '/')
|
||||
|
||||
attack_str = attack_str + "<a href=\"https://attack.mitre.org/techniques/{}/\">{}</a>, ".format(attack_id_dash, attack_id)
|
||||
|
||||
# Chop off last ', '
|
||||
attack_str = attack_str[:len(attack_str) - 2]
|
||||
attack_str = attack_str + "), "
|
||||
|
||||
# Chop off last ', '
|
||||
attack_str = attack_str[:len(attack_str) - 2]
|
||||
attack_str = attack_str + "</b></td>\n"
|
||||
|
||||
lines.insert(11, attack_str)
|
||||
lines.insert(12, "</tr>\n")
|
||||
print(attack_str)
|
||||
|
||||
# Adding Anti-Analysis/Impact Type
|
||||
if args.type:
|
||||
if args.type in impact_types:
|
||||
lines.insert(13, "<tr>\n")
|
||||
lines = insert_if_missing(lines, 14, "<td><b>Impact Type</b></td>\n")
|
||||
|
||||
elif args.type in anti_analysis_types:
|
||||
lines.insert(13, "<tr>\n")
|
||||
lines = insert_if_missing(lines, 14, "<td><b>Anti-Analysis Type</b></td>\n")
|
||||
|
||||
else:
|
||||
raise Exception("Anti-Analysis/Impact Type is not valid")
|
||||
|
||||
lines.insert(15, "<td><b>{}</b></td>\n".format(args.type))
|
||||
lines.insert(16, "</tr>\n")
|
||||
|
||||
offset += 4
|
||||
|
||||
# Adding version field
|
||||
if args.version:
|
||||
lines = insert_if_missing(lines, 13+offset, "<tr>\n")
|
||||
lines = insert_if_missing(lines, 14+offset, "<td><b>Version</b></td>\n")
|
||||
|
||||
lines.insert(15+offset, "<td><b>{}</b></td>\n".format(args.version))
|
||||
lines.insert(16+offset, "</tr>\n")
|
||||
|
||||
# Adding Created field
|
||||
if args.created:
|
||||
lines = insert_if_missing(lines, 17+offset, "<tr>\n")
|
||||
lines = insert_if_missing(lines, 18+offset, "<td><b>Created</b></td>\n")
|
||||
|
||||
created = args.created.replace('_', ' ')
|
||||
lines.insert(19+offset, "<td><b>{}</b></td>\n".format(created))
|
||||
lines.insert(20+offset, "</tr>\n")
|
||||
|
||||
# Adding Last Modified Field
|
||||
if args.last_modified:
|
||||
lines = insert_if_missing(lines, 21+offset, "<tr>\n")
|
||||
lines = insert_if_missing(lines, 22+offset, "<td><b>Last Modified</b></td>\n")
|
||||
|
||||
last_modified = args.last_modified.replace('_', ' ')
|
||||
lines.insert(23+offset, "<td><b>{}</b></td>\n".format(last_modified))
|
||||
lines.insert(24+offset, "</tr>\n")
|
||||
|
||||
lines = insert_if_missing(lines, 25+offset, "</table>\n")
|
||||
lines = insert_if_missing(lines, 26+offset, "\n")
|
||||
|
||||
# Now that modifications are complete, write to file
|
||||
with open(file, "w") as f:
|
||||
for line in lines:
|
||||
f.write(line)
|
||||
|
||||
f.close()
|
||||
|
||||
return
|
||||
|
||||
if __name__=="__main__":
|
||||
main()
|
||||
@@ -1,69 +0,0 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>ID</b></td>
|
||||
<td><b>E1113</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Objective(s)</b></td>
|
||||
<td><b><a href="../collection">Collection</a>, <a href="../credential-access">Credential Access</a></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Related ATT&CK Techniques</b></td>
|
||||
<td><b>Screen Capture (<a href="https://attack.mitre.org/techniques/T1113/">T1113</a>)</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Version</b></td>
|
||||
<td><b>2.0</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Created</b></td>
|
||||
<td><b>1 August 2019</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Last Modified</b></td>
|
||||
<td><b>31 October 2022</b></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# Screen Capture
|
||||
|
||||
Malware takes screen captures of the desktop.
|
||||
|
||||
See ATT&CK: **Screen Capture ([T1113](https://attack.mitre.org/techniques/T1113/))**.
|
||||
|
||||
## Methods
|
||||
|
||||
|Name|ID|Description|
|
||||
|---|---|---|
|
||||
|**WinAPI**|E1113.m01|Screen is captured using WinAPI functions (e.g., user32.GetDesktopWindow).|
|
||||
|
||||
|
||||
## Use in Malware
|
||||
|
||||
|Name|Date|Description|
|
||||
|---|---|---|
|
||||
|[**GotBotKR**](../xample-malware/gobotkr.md)|2019| GoBotKR is capable of capturing screenshots. [[1]](#1)|
|
||||
|[**BlackEnergy**](../xample-malware/blackenergy.md)|2007|Screenshot plugin allows for collection of screenshots [[2]](#2)|
|
||||
|[**DarkComet**](../xample-malware/dark-comet.md)|2008|Can take screenshots of victim's computer [[3]](#3)|
|
||||
|
||||
|
||||
## Detection
|
||||
|
||||
|Tool: capa|Mapping|APIs|
|
||||
|---|---|---|
|
||||
|[check for microsoft office emulation](https://github.com/mandiant/capa-rules/blob/master/anti-analysis/anti-vm/vm-detection/check-for-microsoft-office-emulation.yml)|[Sandbox Detection::Product Key/ID Testing (B0007.005)|CreateFile|
|
||||
|[check for sandbox and av modules](https://github.com/mandiant/capa-rules/blob/master/anti-analysis/anti-av/check-for-sandbox-and-av-modules.yml)|Sandbox Detection (B0007)|GetModuleHandle|
|
||||
|
||||
|Tool: CAPE|Mapping|APIs|
|
||||
|---|---|---|
|
||||
|[antisandbox_joe_anubis_files.py](https://github.com/kevoreilly/community/blob/master/modules/signatures/antisandbox_joe_anubis_files.py)|Sandbox Detection::Check Files (B0007.002)|--|
|
||||
|[antisandbox_cuckoo_files](https://github.com/kevoreilly/community/blob/master/modules/signatures/antisandbox_cuckoo_files.py)|Sandbox Detection::Check Files (B0007.002)|--|
|
||||
|
||||
## References
|
||||
|
||||
<a name="1">[1]</a> https://www.welivesecurity.com/2019/07/08/south-korean-users-backdoor-torrents/
|
||||
|
||||
<a name="2">[2]</a> https://securelist.com/be2-custom-plugins-router-abuse-and-target-profiles/67353/
|
||||
|
||||
<a name="3">[3]</a> https://blog.malwarebytes.com/threat-analysis/2012/06/you-dirty-rat-part-1-darkcomet/
|
||||
@@ -1,60 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def dir_walk(directory):
|
||||
ignorelist = ["nursery", '.github', '.git', 'LICENSE.txt', 'README.md', '.gitattributes', 'ynewsletters', 'yfaq', 'zscript']
|
||||
|
||||
# Iterate recursively over all files in the repo
|
||||
for root, dirs, files in os.walk(directory):
|
||||
if not any(block in root for block in ignorelist):
|
||||
for name in files:
|
||||
f = open(os.path.join(root, name), "r")
|
||||
lines = f.readlines() # Read contents of each capa rule
|
||||
f.close()
|
||||
|
||||
try:
|
||||
index = lines.index('## Use in Malware\n')
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# if lines[index+2] != "|Name|Date|Description|\n":
|
||||
# print("{} has format issue".format(os.path.join(root,name)))
|
||||
|
||||
try:
|
||||
index = lines.index('|Name|Date|Method|Description|\n')
|
||||
except ValueError:
|
||||
print("{} has format issue".format(os.path.join(root,name)))
|
||||
break
|
||||
|
||||
index = lines.index("<td><b>Last Modified</b></td>\n")
|
||||
lines[index+1] = "<td><b>21 November 2022</b></td>\n"
|
||||
|
||||
|
||||
with open(os.path.join(root, name), 'w') as f:
|
||||
for line in lines:
|
||||
f.write(line)
|
||||
f.close()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# for line in lines:
|
||||
# equals = re.search("={3,}\n", line) # Search capa rule for attack mapping, if found, add to attack counter
|
||||
|
||||
# if equals:
|
||||
# print(os.path.join(root, name))
|
||||
# index = lines.index(equals.group(0))
|
||||
# lines.remove(equals.group(0))
|
||||
# lines[index-1] = "# {0}\n".format(lines[index-1])
|
||||
|
||||
|
||||
# with open(os.path.join(root, name), 'w') as f:
|
||||
# for line in lines:
|
||||
# f.write(line)
|
||||
# f.close()
|
||||
|
||||
|
||||
|
||||
dir_walk('..')
|
||||
Reference in New Issue
Block a user