Skip to content

Commit 5f54eb8

Browse files
trautMikaayenson
andauthored
chore: Removing RTAs (#4437)
* Delete RTAs * Delete RTA-related orchestration code * Drop RTAs from tests * Remove RTAs from README * Further cleanup * Readme update * Version bump and no more RTAs * Styling fixes * Drop RTAs from config files * Drop `rule-mapping.yaml` * Bring back event collector / normalizer * Drop rta mention * Cleanup rta leftovers * Style fix --------- Co-authored-by: Mika Ayenson, PhD <[email protected]>
1 parent 49c361d commit 5f54eb8

File tree

659 files changed

+29
-27100
lines changed

Some content is hidden

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

659 files changed

+29
-27100
lines changed

.github/CODEOWNERS

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ tests/**/*.py @mikaayenson @eric-forte-elastic @terrancedejesus
55
detection_rules/ @mikaayenson @eric-forte-elastic @terrancedejesus
66
tests/ @mikaayenson @eric-forte-elastic @terrancedejesus
77
lib/ @mikaayenson @eric-forte-elastic @terrancedejesus
8-
rta/ @mikaayenson @eric-forte-elastic @terrancedejesus
98
hunting/ @mikaayenson @eric-forte-elastic @terrancedejesus
109

1110
# skip rta-mapping to avoid the spam

.github/paths-labeller.yml

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
- "detection_rules/**/*.py"
1313
- "kibana/**/*.py"
1414
- "kql/**/*.py"
15-
- "RTA":
16-
- "rta/**/*"
1715
- "Hunting":
1816
- "hunting/**/*"
1917

.pre-commit-config.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ repos:
66
hooks:
77
- id: flake8
88
args: ['--ignore=D203,C901,E501,W503', '--max-line-length=120','--max-complexity=10', '--statistics']
9-
exclude: '^rta|^kql'
9+
exclude: '^kql'
1010
- repo: https://github.com/PyCQA/bandit
1111
rev: 1.7.4
1212
hooks:
1313
- id: bandit
1414
args: ['-s', 'B101,B603,B404,B607']
15-
exclude: '^rta|^kql'
15+
exclude: '^kql'
1616
# Potential future rigor
1717
# - repo: https://github.com/PyCQA/pylint
1818
# rev: v2.15.6
1919
# hooks:
2020
# - id: pylint
2121
# language: system
22-
# exclude: '^rta|^kql'
22+
# exclude: '^kql'
2323
# - repo: https://github.com/PyCQA/isort
2424
# rev: 5.10.1
2525
# hooks:
26-
# - id: isort
26+
# - id: isort

README.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This repository was first announced on Elastic's blog post, [Elastic Security op
1616
- [Overview of this repository](#overview-of-this-repository)
1717
- [Getting started](#getting-started)
1818
- [How to contribute](#how-to-contribute)
19+
- [RTAs](#rtas)
1920
- [Licensing](#licensing)
2021
- [Questions? Problems? Suggestions?](#questions-problems-suggestions)
2122

@@ -31,7 +32,6 @@ Detection Rules contains more than just static rule files. This repository also
3132
| [`hunting/`](./hunting/) | Root directory where threat hunting package and queries are stored |
3233
| [`kibana/`](lib/kibana) | Python library for handling the API calls to Kibana and the Detection Engine |
3334
| [`kql/`](lib/kql) | Python library for parsing and validating Kibana Query Language |
34-
| [`rta/`](rta) | Red Team Automation code used to emulate attacker techniques, used for rule testing |
3535
| [`rules/`](rules) | Root directory where rules are stored |
3636
| [`rules_building_block/`](rules_building_block) | Root directory where building block rules are stored |
3737
| [`tests/`](tests) | Python code for unit testing rules |
@@ -133,9 +133,14 @@ For more advanced command line interface (CLI) usage, refer to the [CLI guide](C
133133

134134
We welcome your contributions to Detection Rules! Before contributing, please familiarize yourself with this repository, its [directory structure](#overview-of-this-repository), and our [philosophy](PHILOSOPHY.md) about rule creation. When you're ready to contribute, read the [contribution guide](CONTRIBUTING.md) to learn how we turn detection ideas into production rules and validate with testing.
135135

136+
## RTAs
137+
138+
Red Team Automations (RTAs) used to emulate attacker techniques and verify the rules can be found in dedicated
139+
repository - [Cortado](https://github.com/elastic/cortado).
140+
136141
## Licensing
137142

138-
Everything in this repository — rules, code, RTA, etc. — is licensed under the [Elastic License v2](LICENSE.txt). These rules are designed to be used in the context of the Detection Engine within the Elastic Security application. If you’re using our [Elastic Cloud managed service](https://www.elastic.co/cloud/) or the default distribution of the Elastic Stack software that includes the [full set of free features](https://www.elastic.co/subscriptions), you’ll get the latest rules the first time you navigate to the detection engine.
143+
Everything in this repository — rules, code, etc. — is licensed under the [Elastic License v2](LICENSE.txt). These rules are designed to be used in the context of the Detection Engine within the Elastic Security application. If you’re using our [Elastic Cloud managed service](https://www.elastic.co/cloud/) or the default distribution of the Elastic Stack software that includes the [full set of free features](https://www.elastic.co/subscriptions), you’ll get the latest rules the first time you navigate to the detection engine.
139144

140145
Occasionally, we may want to import rules from another repository that already have a license, such as MIT or Apache 2.0. This is welcome, as long as the license permits sublicensing under the Elastic License v2. We keep those license notices in `NOTICE.txt` and sublicense as the Elastic License v2 with all other rules. We also require contributors to sign a [Contributor License Agreement](https://www.elastic.co/contributor-agreement) before contributing code to any Elastic repositories.
141146

detection_rules/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
ghwrap,
2020
kbwrap,
2121
main,
22-
mappings,
2322
ml,
2423
misc,
2524
navigator,
@@ -37,7 +36,6 @@
3736
'eswrap',
3837
'ghwrap',
3938
'kbwrap',
40-
'mappings',
4139
"main",
4240
'misc',
4341
'ml',

detection_rules/cli_utils.py

-3
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,4 @@ def rule_prompt(path=None, rule_type=None, required_only=True, save=True, verbos
265265
print('Did not set the following values because they are un-required when set to the default value')
266266
print(' - {}'.format('\n - '.join(skipped)))
267267

268-
# rta_mappings.add_rule_to_mapping_file(rule)
269-
# click.echo('Placeholder added to rule-mapping.yaml')
270-
271268
return rule

detection_rules/docs.py

-22
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ def populate(self):
7777
self.add_summary()
7878
self.add_rule_details()
7979
self.add_attack_matrix()
80-
self.add_rta_mapping()
8180
self.add_rule_details(self.deprecated_rules, 'Deprecated Rules')
8281

8382
def add_summary(self):
@@ -172,27 +171,6 @@ def add_rule_details(self, rules: Optional[Union[DeprecatedCollection, RuleColle
172171

173172
worksheet.autofilter(0, 0, len(rules) + 1, len(headers) - 1)
174173

175-
def add_rta_mapping(self):
176-
"""Add a worksheet for the RTA/Rule RTA mapping."""
177-
from .rule_loader import rta_mappings
178-
179-
worksheet = self.add_worksheet('RTA Mapping')
180-
worksheet.freeze_panes(1, 0)
181-
headers = ('Rule ID', 'Rule Name', 'RTA')
182-
for column, header in enumerate(headers):
183-
worksheet.write(0, column, header, self.default_header_format)
184-
185-
row = 1
186-
for rule_id, mapping in rta_mappings.get_rta_mapping().items():
187-
worksheet.write(row, 0, rule_id)
188-
worksheet.write(row, 1, mapping['rule_name'])
189-
worksheet.write(row, 2, mapping['rta_name'])
190-
row += 1
191-
192-
worksheet.set_column(0, 0, 35)
193-
worksheet.set_column(1, 1, 50)
194-
worksheet.set_column(2, 2, 35)
195-
196174
def add_attack_matrix(self):
197175
"""Add a worksheet for ATT&CK coverage."""
198176
worksheet = self.add_worksheet(attack_tm + ' Coverage')

detection_rules/eswrap.py

+13-18
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
from .main import root
2222
from .misc import add_params, client_error, elasticsearch_options, get_elasticsearch_client, nested_get
2323
from .rule import TOMLRule
24-
from .rule_loader import rta_mappings, RuleCollection
25-
from .utils import format_command_options, normalize_timing_and_sort, unix_time_to_formatted, get_path
2624

25+
from .rule_loader import RuleCollection
26+
from .utils import format_command_options, normalize_timing_and_sort, unix_time_to_formatted, get_path
2727

2828
COLLECTION_DIR = get_path('collections')
2929
MATCH_ALL = {'bool': {'filter': [{'match_all': {}}]}}
@@ -60,7 +60,7 @@ def parse_unique_field_results(rule_type: str, unique_fields: List[str], search_
6060
return {'results': parsed_results} if parsed_results else {}
6161

6262

63-
class RtaEvents:
63+
class Events:
6464
"""Events collected from Elasticsearch."""
6565

6666
def __init__(self, events):
@@ -87,7 +87,7 @@ def _get_dump_dir(rta_name=None, host_id=None, host_os_family=None):
8787
os.makedirs(dump_dir, exist_ok=True)
8888
return dump_dir
8989

90-
def evaluate_against_rule_and_update_mapping(self, rule_id, rta_name, verbose=True):
90+
def evaluate_against_rule(self, rule_id, verbose=True):
9191
"""Evaluate a rule against collected events and update mapping."""
9292
from .utils import combine_sources, evaluate
9393

@@ -96,15 +96,10 @@ def evaluate_against_rule_and_update_mapping(self, rule_id, rta_name, verbose=Tr
9696
merged_events = combine_sources(*self.events.values())
9797
filtered = evaluate(rule, merged_events, normalize_kql_keywords=RULES_CONFIG.normalize_kql_keywords)
9898

99-
if filtered:
100-
sources = [e['agent']['type'] for e in filtered]
101-
mapping_update = rta_mappings.add_rule_to_mapping_file(rule, len(filtered), rta_name, *sources)
99+
if verbose:
100+
click.echo('Matching results found')
102101

103-
if verbose:
104-
click.echo('Updated rule-mapping file with: \n{}'.format(json.dumps(mapping_update, indent=2)))
105-
else:
106-
if verbose:
107-
click.echo('No updates to rule-mapping file; No matching results')
102+
return filtered
108103

109104
def echo_events(self, pager=False, pretty=True):
110105
"""Print events to stdout."""
@@ -322,8 +317,8 @@ def count_from_rule(self, rules: RuleCollection, start_time=None, end_time='now'
322317
return survey_results
323318

324319

325-
class CollectRtaEvents(CollectEvents):
326-
"""Collect RTA events from elasticsearch."""
320+
class CollectEventsWithDSL(CollectEvents):
321+
"""Collect events from elasticsearch."""
327322

328323
@staticmethod
329324
def _group_events_by_type(events):
@@ -340,15 +335,15 @@ def run(self, dsl, indexes, start_time):
340335
results = self.search(dsl, language='dsl', index=indexes, start_time=start_time, end_time='now', size=5000,
341336
sort=[{'@timestamp': {'order': 'asc'}}])
342337
events = self._group_events_by_type(results)
343-
return RtaEvents(events)
338+
return Events(events)
344339

345340

346341
@root.command('normalize-data')
347342
@click.argument('events-file', type=click.File('r'))
348343
def normalize_data(events_file):
349344
"""Normalize Elasticsearch data timestamps and sort."""
350345
file_name = os.path.splitext(os.path.basename(events_file.name))[0]
351-
events = RtaEvents({file_name: [json.loads(e) for e in events_file.readlines()]})
346+
events = Events({file_name: [json.loads(e) for e in events_file.readlines()]})
352347
events.save(dump_dir=os.path.dirname(events_file.name))
353348

354349

@@ -383,15 +378,15 @@ def collect_events(ctx, host_id, query, index, rta_name, rule_id, view_events):
383378
dsl['bool'].setdefault('filter', []).append({'bool': {'should': [{'match_phrase': {'host.id': host_id}}]}})
384379

385380
try:
386-
collector = CollectRtaEvents(client)
381+
collector = CollectEventsWithDSL(client)
387382
start = time.time()
388383
click.pause('Press any key once detonation is complete ...')
389384
start_time = f'now-{round(time.time() - start) + 5}s'
390385
events = collector.run(dsl, index or '*', start_time)
391386
events.save(rta_name=rta_name, host_id=host_id)
392387

393388
if rta_name and rule_id:
394-
events.evaluate_against_rule_and_update_mapping(rule_id, rta_name)
389+
events.evaluate_against_rule(rule_id)
395390

396391
if view_events and events.events:
397392
events.echo_events(pager=True)

detection_rules/etc/rule-mapping.yaml

-2
This file was deleted.

detection_rules/main.py

-27
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
from .generic_loader import GenericCollection
2828
from .exception import (TOMLExceptionContents,
2929
build_exception_objects, parse_exceptions_results_from_api)
30-
from .mappings import build_coverage_map, get_triggered_rules, print_converage_summary
3130
from .misc import (
3231
add_client, client_error, nested_set, parse_user_config
3332
)
@@ -696,29 +695,3 @@ def prep_rule(author: str):
696695
updated_rule.write_text(json.dumps(template_rule, sort_keys=True))
697696
click.echo(f'Rule saved to: {updated_rule}. Import this to Kibana to create alerts on all dnstwist-* indexes')
698697
click.echo('Note: you only need to import and enable this rule one time for all dnstwist-* indexes')
699-
700-
701-
@root.group('rta')
702-
def rta_group():
703-
"""Commands related to Red Team Automation (RTA) scripts."""
704-
705-
706-
# create command to show rule-rta coverage
707-
@rta_group.command('coverage')
708-
@click.option("-o", "--os-filter", default="all",
709-
help="Filter rule coverage summary by OS. (E.g. windows) Default: all")
710-
def rta_coverage(os_filter: str):
711-
"""Show coverage of RTA / rules by os type."""
712-
713-
# get all rules
714-
all_rules = RuleCollection.default()
715-
716-
# get rules triggered by RTA
717-
triggered_rules = get_triggered_rules()
718-
719-
# build coverage map
720-
coverage_map = build_coverage_map(triggered_rules, all_rules)
721-
722-
# # print summary
723-
all_rule_count = len(all_rules.rules)
724-
print_converage_summary(coverage_map, all_rule_count, os_filter)

0 commit comments

Comments
 (0)