Skip to content

Commit d6b4aab

Browse files
Merge branch 'main' into cli/export-rule
2 parents d54fa3e + aaf6a0f commit d6b4aab

File tree

570 files changed

+80601
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

570 files changed

+80601
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"script": {"lang": "painless", "source": "\nString nGramAtPosition(String text, int fieldcount, int n){\n if (fieldcount+n>text.length()){\n return null;\n }\n else \n {\n return text.substring(fieldcount, fieldcount+n);\n } \n}\n\nString[] secondLevelDomain(Map dynamic_domains, String domain, String subdomain, String registered_domain, String top_level_domain){\n\n if (registered_domain == null || registered_domain == '.') {\n return new String[] {'', ''};\n }\n\n if (dynamic_domains.containsKey(registered_domain) == true) {\n if (subdomain != null) {\n return new String[] {subdomain, registered_domain};\n }\n } \n\n return new String[] {registered_domain.substring(0, registered_domain.length()-top_level_domain.length()-1), top_level_domain};\n}\n\nString domain = ctx['dns']['question']['name'];\nString subdomain = ctx['dns']['question']['subdomain'];\nString registered_domain = ctx['dns']['question']['registered_domain'];\nString top_level_domain = ctx['dns']['question']['top_level_domain'];\n\nString[] ret = secondLevelDomain(params.dynamic_domains, domain, subdomain, registered_domain, top_level_domain);\n\nString sld = ret[0];\nString tld = ret[1];\n\nctx['f'] = new HashMap();\nctx['f']['tld'] = tld;\n\nfor (int i=0;i<sld.length();i++){\n String field = nGramAtPosition(sld, i, 1);\n if (field == null) {\n break;\n }\n ctx['f']['u'+ Integer.toString(i)] = field;\n}\nfor (int i=0;i<sld.length();i++){\n String field = nGramAtPosition(sld, i, 2);\n if (field == null) {\n break;\n }\n ctx['f']['b'+ Integer.toString(i)] = field;\n}\nfor (int i=0;i<sld.length();i++){\n String field = nGramAtPosition(sld, i, 3);\n if (field == null) {\n break;\n }\n ctx['f']['t'+ Integer.toString(i)] = field;\n}\n"}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"script": {"lang": "painless", "source": "\n def top_classes = ctx['ml_is_dga']['top_classes'];\n def malicious_probability = 0.0;\n def malicious_prediction = ctx['ml_is_dga']['malicious_prediction'];\n for (def class: top_classes) {\n if (class['class_name'] == 1) {\n malicious_probability = class['class_probability'];\n }\n }\n ctx.remove('ml_is_dga');\n ctx.remove('f');\n ctx['ml_is_dga'] = new HashMap();\n ctx['ml_is_dga']['malicious_prediction'] = malicious_prediction;\n ctx['ml_is_dga']['malicious_probability'] = malicious_probability;\n "}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"description": "A pipeline of pipelines for enriching DNS data. Ignores non-DNS data.", "processors": [{"pipeline": {"if": "ctx.type=='dns'", "name": "dns_dga_inference_enrich_pipeline"}}], "version": 1}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"processors": [{"script": {"id": "dga_ngrams_create", "params": {"dynamic_domains": {"avsvmcloud.com": 0, "co.cc": 0, "cz.cc": 0, "ddns.net": 0, "dyndns.org": 0, "dynserv.com": 0, "github.io": 0, "mooo.com": 0, "mynumber.org": 0, "yi.org": 0}}}}, {"inference": {"field_map": {}, "inference_config": {"classification": {"num_top_classes": 1}}, "model_id": "dga_875109_1.0", "target_field": "ml_is_dga"}}, {"script": {"id": "dga_ngrams_transform_delete"}}]}

ML-models/DGA/ML-DGA-20201216-1/dga_875109_1.0_model.json

+1
Large diffs are not rendered by default.

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ This repository was first announced on Elastic's blog post, [Elastic Security op
1212
## Table of Contents
1313
- [Overview of this repository](#overview-of-this-repository)
1414
- [Getting started](#getting-started)
15+
- [Red Team Automation](rta)
1516
- [How to contribute](#how-to-contribute)
1617
- [Licensing](#licensing)
18+
- [Questions? Problems? Suggestions?](#questions-problems-suggestions)
1719

1820

1921
## Overview of this repository

rta/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Red Team Automation
2+
3+
[![Supported Python versions](https://img.shields.io/badge/python-3.7+-yellow.svg)](https://www.python.org/downloads/)
4+
[![Chat](https://img.shields.io/badge/chat-%23security--detection--rules-blueviolet)](https://ela.st/slack)
5+
6+
The repo comes with some red team automation ([RTA](./)) python scripts that run on Windows, Mac OS, and \*nix.
7+
RTA scripts emulate known attacker behaviors and are an easy way too verify that your rules are active and working as expected.
8+
9+
```console
10+
$ python -m rta -h
11+
usage: rta [-h] ttp_name
12+
13+
positional arguments:
14+
ttp_name
15+
16+
optional arguments:
17+
-h, --help show this help message and exit
18+
```
19+
<<<<<<< HEAD
20+
ttp_name can be found in the [rta](./rta) directory. For example to execute `./rta/wevtutil_log_clear.py` script, run command:
21+
=======
22+
`ttp_name` is the name of a script that can be found in the [rta](./rta) directory. For example, to execute ./rta/wevtutil_log_clear.py script, run command:
23+
>>>>>>> 607d8cf06a298ec4edf8c0e326b3c1a9ace509ea
24+
25+
```console
26+
$ python -m rta wevtutil_log_clear
27+
```
28+
29+
Most of the RTA scripts contain a comment with the rule name, in `signal.rule.name`, that maps to the Kibana Detection Signals.

rta/__init__.py.bak

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
import glob
6+
import importlib
7+
import os
8+
9+
from . import common
10+
11+
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
12+
13+
14+
def get_ttp_list(os_types=None):
15+
scripts = []
16+
if os_types and not isinstance(os_types, (list, tuple)):
17+
os_types = [os_types]
18+
19+
for script in sorted(glob.glob(os.path.join(CURRENT_DIR, "*.py"))):
20+
base_name, _ = os.path.splitext(os.path.basename(script))
21+
if base_name not in ("common", "main") and not base_name.startswith("_"):
22+
if os_types:
23+
# Import it and skip it if it's not supported
24+
importlib.import_module(__name__ + "." + base_name)
25+
if not any(base_name in common.OS_MAPPING[os_type] for os_type in os_types):
26+
continue
27+
28+
scripts.append(script)
29+
30+
return scripts
31+
32+
33+
def get_ttp_names(os_types=None):
34+
names = []
35+
for script in get_ttp_list(os_types):
36+
basename, ext = os.path.splitext(os.path.basename(script))
37+
names.append(basename)
38+
return names
39+
40+
41+
__all__ = (
42+
"common"
43+
)

rta/__main__.py.bak

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
import argparse
6+
import importlib
7+
import os
8+
9+
from . import get_ttp_names
10+
11+
parser = argparse.ArgumentParser("rta")
12+
parser.add_argument("ttp_name")
13+
14+
parsed_args, remaining = parser.parse_known_args()
15+
ttp_name, _ = os.path.splitext(os.path.basename(parsed_args.ttp_name))
16+
17+
if ttp_name not in get_ttp_names():
18+
raise ValueError("Unknown RTA {}".format(ttp_name))
19+
20+
module = importlib.import_module("rta." + ttp_name)
21+
exit(module.main(*remaining))

rta/adobe_hijack.py.bak

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
# Name: Adobe Hijack Persistence
6+
# RTA: adobe_hijack.py
7+
# ATT&CK: T1044
8+
# Description: Replaces PE file that will run on Adobe Reader start.
9+
10+
import os
11+
12+
from . import common
13+
14+
15+
@common.requires_os(common.WINDOWS)
16+
def main():
17+
rdr_cef_dir = "C:\\Program Files (x86)\\Adobe\\Acrobat Reader DC\\Reader\\AcroCEF"
18+
rdrcef_exe = os.path.join(rdr_cef_dir, "RdrCEF.exe")
19+
cmd_path = "C:\\Windows\\System32\\cmd.exe"
20+
backup = os.path.abspath("xxxxxx")
21+
backedup = False
22+
23+
# backup original if it exists
24+
if os.path.isfile(rdrcef_exe):
25+
common.log("{} already exists, backing up file.".format(rdrcef_exe))
26+
common.copy_file(rdrcef_exe, backup)
27+
backedup = True
28+
else:
29+
common.log("{} doesn't exist. Creating path.".format(rdrcef_exe))
30+
os.makedirs(rdr_cef_dir)
31+
32+
# overwrite original
33+
common.copy_file(cmd_path, rdrcef_exe)
34+
35+
# cleanup
36+
if backedup:
37+
common.log("Putting back backup copy.")
38+
common.copy_file(backup, rdrcef_exe)
39+
os.remove(backup)
40+
else:
41+
common.remove_file(rdrcef_exe)
42+
os.removedirs(rdr_cef_dir)
43+
44+
45+
if __name__ == "__main__":
46+
exit(main())

rta/appcompat_shim.py.bak

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
# Name: Application Compatibility Shims
6+
# RTA: appcompat_shim.py
7+
# ATT&CK: T1138
8+
# Description: Use sdbinst.exe to install a binary patch/application shim.
9+
10+
import time
11+
12+
from . import common
13+
14+
SHIM_FILE = common.get_path("bin", "CVE-2013-3893.sdb")
15+
16+
17+
@common.requires_os(common.WINDOWS)
18+
@common.dependencies(SHIM_FILE)
19+
def main():
20+
common.log("Application Compatibility Shims")
21+
22+
common.execute(["sdbinst.exe", "-q", "-p", SHIM_FILE])
23+
time.sleep(2)
24+
25+
common.log("Removing installed shim", log_type="-")
26+
common.execute(["sdbinst.exe", "-u", SHIM_FILE])
27+
28+
29+
if __name__ == "__main__":
30+
exit(main())

rta/at_command.py.bak

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
# Name: AT Command Lateral Movement
6+
# RTA: at_command.py
7+
# ATT&CK: T1053
8+
# Description: Enumerates at tasks on target host, and schedules an at job for one hour in the future. Then checks the
9+
# status of that task, and deletes the task.
10+
11+
import datetime
12+
import re
13+
import sys
14+
15+
from . import common
16+
17+
18+
@common.requires_os(common.WINDOWS)
19+
def main(target_host=None):
20+
target_host = target_host or common.get_ip()
21+
host_str = '\\\\%s' % target_host
22+
23+
# Current time at \\localhost is 11/16/2017 11:25:50 AM
24+
code, output = common.execute(['net', 'time', host_str])
25+
match = re.search(r'Current time at .*? is (\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+) (AM|PM)', output)
26+
groups = match.groups()
27+
m, d, y, hh, mm, ss, period = groups
28+
now = datetime.datetime(month=int(m), day=int(d), year=int(y), hour=int(hh), minute=int(mm), second=int(ss))
29+
if period == 'PM' and hh != '12':
30+
now += datetime.timedelta(hours=12)
31+
32+
# Add one hour
33+
task_time = now + datetime.timedelta(hours=1)
34+
35+
# Round down minutes
36+
time_string = '%d:%d' % (task_time.hour, task_time.minute)
37+
38+
# Enumerate all remote tasks
39+
common.execute(['at.exe', host_str])
40+
41+
# Create a job 1 hour into the future
42+
code, output = common.execute(['at', host_str, time_string, 'cmd /c echo hello world'])
43+
44+
if code == 1 and 'deprecated' in output:
45+
common.log("Unable to continue RTA. Not supported in this version of Windows")
46+
return common.UNSUPPORTED_RTA
47+
48+
if code == 0:
49+
job_id = re.search('ID = (\d+)', output).group(1)
50+
51+
# Check status and delete
52+
common.execute(['at.exe', host_str, job_id])
53+
common.execute(['at.exe', host_str, job_id, '/delete'])
54+
55+
56+
if __name__ == "__main__":
57+
exit(main(*sys.argv[1:]))

rta/bitsadmin_download.py.bak

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
# Name: Suspicious BitsAdmin Download File
6+
# RTA: bitsadmin_download.py
7+
# ATT&CK: T1197
8+
# Description: Runs BitsAdmin to download file via command line.
9+
10+
11+
import os
12+
import subprocess
13+
14+
from . import common
15+
16+
17+
@common.requires_os(common.WINDOWS)
18+
def main():
19+
common.log("Running Windows BitsAdmin to Download")
20+
server, ip, port = common.serve_web()
21+
url = "http://" + ip + ":" + str(port) + "/bin/myapp.exe"
22+
dest_path = os.path.abspath("myapp-test.exe")
23+
fake_word = os.path.abspath("winword.exe")
24+
25+
common.log("Emulating parent process: {parent}".format(parent=fake_word))
26+
common.copy_file("C:\\Windows\\System32\\cmd.exe", fake_word)
27+
28+
command = subprocess.list2cmdline(['bitsadmin.exe', '/Transfer', '/Download', url, dest_path])
29+
common.execute([fake_word, "/c", command], timeout=15, kill=True)
30+
common.execute(['taskkill', '/f', '/im', 'bitsadmin.exe'])
31+
32+
common.remove_files(dest_path, fake_word)
33+
34+
35+
if __name__ == "__main__":
36+
exit(main())

rta/brute_force_login.py.bak

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
# Name: Brute Force Login Attempts
6+
# RTA: brute_force_login.py
7+
# ATT&CK: T1110
8+
# Description: Simulates brute force or password spraying tactics.
9+
# Remote audit failures must be enabled to trigger: `auditpol /set /subcategory:"Logon" /failure:enable`
10+
11+
import random
12+
import string
13+
import sys
14+
import time
15+
16+
from . import common
17+
18+
19+
@common.requires_os(common.WINDOWS)
20+
def main(username="rta-tester", remote_host=None):
21+
if not remote_host:
22+
common.log('A remote host is required to detonate this RTA', '!')
23+
return common.MISSING_REMOTE_HOST
24+
25+
common.enable_logon_auditing(remote_host)
26+
27+
common.log('Brute forcing login with invalid password against {}'.format(remote_host))
28+
ps_command = '''
29+
$PW = ConvertTo-SecureString "such-secure-passW0RD!" -AsPlainText -Force
30+
$CREDS = New-Object System.Management.Automation.PsCredential {username}, $PW
31+
Invoke-WmiMethod -ComputerName {host} -Class Win32_process -Name create -ArgumentList ipconfig -Credential $CREDS
32+
'''
33+
command = ['powershell', '-c', ps_command.format(username=username, host=remote_host)]
34+
35+
# fail 4 times - the first 3 concurrently and wait for the final to complete
36+
for i in range(4):
37+
common.execute(command, wait=i == 3)
38+
39+
time.sleep(1)
40+
41+
common.log('Password spraying against {}'.format(remote_host))
42+
43+
# fail 5 times - the first 4 concurrently and wait for the final to complete
44+
for i in range(5):
45+
random_user = ''.join(random.sample(string.ascii_letters, 10))
46+
command = ['powershell', '-c', ps_command.format(username=random_user, host=remote_host)]
47+
common.execute(command, wait=i == 4)
48+
49+
# allow time for audit event to process
50+
time.sleep(2)
51+
52+
53+
if __name__ == "__main__":
54+
exit(main(*sys.argv[1:]))

rta/certutil_file_obfuscation.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Name: Certutil Encode / Decode
66
# RTA: certutil_file_obfuscation.py
77
# ATT&CK: T1140
8+
# signal.rule.name: Encoding or Decoding Files via CertUtil
89
# Description: Uses certutil to create an encoded copy of cmd.exe. Then uses certutil to decode that copy.
910

1011
import os

rta/certutil_file_obfuscation.py.bak

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License;
3+
# you may not use this file except in compliance with the Elastic License.
4+
5+
# Name: Certutil Encode / Decode
6+
# RTA: certutil_file_obfuscation.py
7+
# ATT&CK: T1140
8+
# Elastic Detection: Encoding or Decoding Files via CertUtil
9+
# Description: Uses certutil to create an encoded copy of cmd.exe. Then uses certutil to decode that copy.
10+
11+
import os
12+
13+
from . import common
14+
15+
16+
@common.requires_os(common.WINDOWS)
17+
def main():
18+
common.log("Encoding target")
19+
encoded_file = os.path.abspath('encoded.txt')
20+
decoded_file = os.path.abspath('decoded.exe')
21+
common.execute(["c:\\Windows\\System32\\certutil.exe", "-encode", "c:\\windows\\system32\\cmd.exe", encoded_file])
22+
23+
common.log("Decoding target")
24+
common.execute(["c:\\Windows\\System32\\certutil.exe", "-decode", encoded_file, decoded_file])
25+
26+
common.log("Cleaning up")
27+
common.remove_file(encoded_file)
28+
common.remove_file(decoded_file)
29+
30+
31+
if __name__ == "__main__":
32+
exit(main())

0 commit comments

Comments
 (0)