Skip to content

[Bug] Strip Non-Public Fields Prior to Uploading Rules #2986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

Mikaayenson
Copy link
Contributor

@Mikaayenson Mikaayenson commented Jul 31, 2023

Issues

Resolves #2931
Resolves #2930

Summary

Some fields are not public using the API. This PR removes these fields based on stack schema versions (min and max) so that the fields aren't used when trying to upload the rule to a different stack version (see the issue for more details).

  • Uses a stack version (user provided or pulled from rule or default to 8.3.0) to remove non public fields
  • Adds a new strip_non_public_fields method to init and NON_PUBLIC_FIELDS variable in definitions
  • Strips the fields prior to uploading.
  • Rewrites downgrade_contents_from_rule to downgrade and validate the to_dict version of the rule, then builds and new TOMLRuleContents object to obtain a to_api_format payload prior to uploading.

Testing

Testing on Old Branches

The report mentioned this issue occurred when trying to use an old version of out rules (on an older stack) to a future stack.
In testing I switched to the 8.8 branch since my cloud stack is 8.9 and tried to upload.

Test Failure Prior to Code Changes

█▀▀▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄   ▄      █▀▀▄ ▄  ▄ ▄   ▄▄▄ ▄▄▄
█  █ █▄▄  █  █▄▄ █    █   █  █ █ █▀▄ █      █▄▄▀ █  █ █   █▄▄ █▄▄
█▄▄▀ █▄▄  █  █▄▄ █▄▄  █  ▄█▄ █▄█ █ ▀▄█      █ ▀▄ █▄▄█ █▄▄ █▄▄ ▄▄█

{"statusCode":400,"error":"Bad Request","message":"[request body]: invalid keys \"related_integrations,[{\"package\":\"endpoint\",\"version\":\"^8.2.0\"}],required_fields,[{\"name\":\"destination.as.organization.name\",\"type\":\"keyword\",\"ecs\":true},{\"name\":\"dns.question.name\",\"type\":\"keyword\",\"ecs\":true},{\"name\":\"dns.question.type\",\"type\":\"keyword\",\"ecs\":true},{\"name\":\"event.action\",\"type\":\"keyword\",\"ecs\":true},{\"name\":\"file.extension\",\"type\":\"keyword\",\"ecs\":true},{\"name\":\"host.id\",\"type\":\"keyword\",\"ecs\":true},{\"name\":\"process.args\",\"type\":\"keyword\",\"ecs\":true},{\"name\":\"process.entity_id\",\"type\":\"keyword\",\"ecs\":true}]\""}
Traceback (most recent call last):
  File "/opt/homebrew/Cellar/[email protected]/3.10.12_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/opt/homebrew/Cellar/[email protected]/3.10.12_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
    cli.main()
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
    run()
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 317, in run_module
    run_module_as_main(options.target, alter_argv=True)
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 238, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/__main__.py", line 34, in <module>
    main()
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/__main__.py", line 31, in main
    root(prog_name="detection_rules")
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/cli_utils.py", line 94, in get_collection
    return f(*args, **kwargs)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/kbwrap.py", line 63, in upload_rule
    results = RuleResource.bulk_create(api_payloads)
  File "/Users/stryker/workspace/Elastic/detection-rules/kibana/resources.py", line 27, in bulk_create
    responses = Kibana.current().post(cls.BASE_URI + "/_bulk_create", data=resources)
  File "/Users/stryker/workspace/Elastic/detection-rules/kibana/connector.py", line 117, in post
    return self.request('POST', uri, params=params, data=data, error=error, **kwargs)
  File "/Users/stryker/workspace/Elastic/detection-rules/kibana/connector.py", line 96, in request
    response.raise_for_status()
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/requests/models.py", line 960, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://<redacted>/api/detection_engine/rules/_bulk_create

Passing

-m detection_rules kibana upload-rule -id a8afdce2-0ec1-11ee-b843-f661ea17fbcd -r 
Loaded config file: /Users/stryker/workspace/Elastic/detection-rules/.detection-rules-cfg.json

█▀▀▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄   ▄      █▀▀▄ ▄  ▄ ▄   ▄▄▄ ▄▄▄
█  █ █▄▄  █  █▄▄ █    █   █  █ █ █▀▄ █      █▄▄▀ █  █ █   █▄▄ █▄▄
█▄▄▀ █▄▄  █  █▄▄ █▄▄  █  ▄█▄ █▄█ █ ▀▄█      █ ▀▄ █▄▄█ █▄▄ █▄▄ ▄▄█

Successful uploads:
  - 30b0e855-6ebc-457e-85ed-c33e52d0c6fc

Testing on New Terms Rule

Test Failure Prior to Code Changes

 -m detection_rules kibana upload-rule -id a00681e3-9ed6-447c-ab2c-be648821c622 -r 
Loaded config file: /Users/stryker/workspace/Elastic/detection-rules/.detection-rules-cfg.json

█▀▀▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄   ▄      █▀▀▄ ▄  ▄ ▄   ▄▄▄ ▄▄▄
█  █ █▄▄  █  █▄▄ █    █   █  █ █ █▀▄ █      █▄▄▀ █  █ █   █▄▄ █▄▄
█▄▄▀ █▄▄  █  █▄▄ █▄▄  █  ▄█▄ █▄█ █ ▀▄█      █ ▀▄ █▄▄█ █▄▄ █▄▄ ▄▄█

Traceback (most recent call last):
  File "/opt/homebrew/Cellar/[email protected]/3.10.12_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/opt/homebrew/Cellar/[email protected]/3.10.12_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
    cli.main()
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
    run()
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 317, in run_module
    run_module_as_main(options.target, alter_argv=True)
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 238, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/Users/stryker/.vscode/extensions/ms-python.python-2023.12.0/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/__main__.py", line 34, in <module>
    main()
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/__main__.py", line 31, in main
    root(prog_name="detection_rules")
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/cli_utils.py", line 94, in get_collection
    return f(*args, **kwargs)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/kbwrap.py", line 54, in upload_rule
    payload = downgrade(payload, target_version=kibana.version)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/schemas/__init__.py", line 282, in downgrade
    api_contents = migrations[str(version)](version, api_contents)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/schemas/__init__.py", line 260, in migrate_to_8_9
    return strip_additional_properties(version, api_contents)
  File "/Users/stryker/workspace/Elastic/detection-rules/detection_rules/schemas/__init__.py", line 79, in strip_additional_properties
    jsonschema.validate(stripped, target_schema)
  File "/Users/stryker/.virtualenvs/detection_dev/lib/python3.10/site-packages/jsonschema/validators.py", line 934, in validate
    raise error
jsonschema.exceptions.ValidationError: 'new_terms' is a required property

Failed validating 'required' in schema:
    {'$schema': 'http://json-schema.org/draft-04/schema#',
     'additionalProperties': False,
     'properties': {'actions': {'items': {'type': ['string',
                                                   'number',
                                                   'object',
                                                   'array',
                                                   'boolean']},
                                'type': 'array'},
                    'alert_suppression': {'additionalProperties': False,
                                          'properties': {'duration': {'additionalProperties': False,
                                                                      'properties': {'unit': {'enum': ['s',
                                                                                                       'm',
                                                                                                       'h'],
                                                                                              'enumNames': [],
                                                                                              'type': 'string'},
                                                                                     'value': {'format': 'integer',
                                                                                               'type': 'number'}},
                                                                      'required': ['unit',
                                                                                   'value'],
                                                                      'type': 'object'},
                                                         'group_by': {'items': {'description': 'NonEmptyStr',
                                                                                'minLength': 1,
                                                                                'type': 'string'},
                                                                      'type': 'array'}},
                                          'required': ['group_by'],
                                          'type': 'object'},
                    'author': {'items': {'type': 'string'},
                               'type': 'array'},
                    'building_block_type': {'enum': ['default'],
                                            'type': 'string'},
                    'description': {'type': 'string'},
                    'enabled': {'type': 'boolean'},
                    'exceptions_list': {'items': {'type': ['string',
                                                           'number',
                                                           'object',
                                                           'array',
                                                           'boolean']},
                                        'type': 'array'},
                    'false_positives': {'items': {'type': 'string'},
                                        'type': 'array'},
                    'filters': {'items': {'additionalProperties': {'type': ['string',
                                                                            'number',
                                                                            'object',
                                                                            'array',
                                                                            'boolean']},
                                          'type': 'object'},
                                'type': 'array'},
                    'from': {'type': 'string'},
                    'index': {'items': {'type': 'string'}, 'type': 'array'},
                    'interval': {'description': 'Interval',
                                 'pattern': '^\\d+[mshd]$',
                                 'type': 'string'},
                    'language': {'enum': ['kuery', 'lucene'],
                                 'enumNames': [],
                                 'type': 'string'},
                    'license': {'type': 'string'},
                    'max_signals': {'description': 'MaxSignals',
                                    'format': 'integer',
                                    'minimum': 1,
                                    'type': 'number'},
                    'meta': {'additionalProperties': {'type': ['string',
                                                               'number',
                                                               'object',
                                                               'array',
                                                               'boolean']},
                             'type': 'object'},
                    'name': {'description': 'RuleName',
                             'pattern': '^[a-zA-Z0-9].+?[a-zA-Z0-9()]$',
                             'type': 'string'},
                    'new_terms': {'additionalProperties': False,
                                  'properties': {'field': {'description': 'NonEmptyStr',
                                                           'minLength': 1,
                                                           'type': 'string'},
                                                 'history_window_start': {'items': {'additionalProperties': False,
                                                                                    'properties': {'field': {'description': 'NonEmptyStr',
                                                                                                             'minLength': 1,
                                                                                                             'type': 'string'},
                                                                                                   'value': {'description': 'NonEmptyStr',
                                                                                                             'minLength': 1,
                                                                                                             'type': 'string'}},
                                                                                    'required': ['field',
                                                                                                 'value'],
                                                                                    'type': 'object'},
                                                                          'type': 'array'},
                                                 'value': {'description': 'NewTermsFields',
                                                           'items': {'description': 'NonEmptyStr',
                                                                     'minLength': 1,
                                                                     'type': 'string'},
                                                           'maxItems': 3,
                                                           'minItems': 1,
                                                           'type': 'array'}},
                                  'required': ['field',
                                               'history_window_start',
                                               'value'],
                                  'type': 'object'},
                    'note': {'description': 'MarkdownField',
                             'type': 'string'},
                    'query': {'type': 'string'},
                    'references': {'items': {'type': 'string'},
                                   'type': 'array'},
                    'related_integrations': {'items': {'additionalProperties': False,
                                                       'properties': {'integration': {'description': 'NonEmptyStr',
                                                                                      'minLength': 1,
                                                                                      'type': 'string'},
                                                                      'package': {'description': 'NonEmptyStr',
                                                                                  'minLength': 1,
                                                                                  'type': 'string'},
                                                                      'version': {'description': 'NonEmptyStr',
                                                                                  'minLength': 1,
                                                                                  'type': 'string'}},
                                                       'required': ['package',
                                                                    'version'],
                                                       'type': 'object'},
                                             'min_compat': '8.3',
                                             'type': 'array'},
                    'required_fields': {'items': {'additionalProperties': False,
                                                  'properties': {'ecs': {'type': 'boolean'},
                                                                 'name': {'description': 'NonEmptyStr',
                                                                          'minLength': 1,
                                                                          'type': 'string'},
                                                                 'type': {'description': 'NonEmptyStr',
                                                                          'minLength': 1,
                                                                          'type': 'string'}},
                                                  'required': ['ecs',
                                                               'name',
                                                               'type'],
                                                  'type': 'object'},
                                        'min_compat': '8.3',
                                        'type': 'array'},
                    'risk_score': {'description': 'MaxSignals',
                                   'format': 'integer',
                                   'maximum': 100,
                                   'minimum': 1,
                                   'type': 'number'},
                    'risk_score_mapping': {'items': {'additionalProperties': False,
                                                     'properties': {'field': {'type': 'string'},
                                                                    'operator': {'enum': ['equals'],
                                                                                 'type': 'string'},
                                                                    'value': {'type': 'string'}},
                                                     'required': ['field'],
                                                     'type': 'object'},
                                           'type': 'array'},
                    'rule_id': {'description': 'UUIDString',
                                'pattern': '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
                                'type': 'string'},
                    'rule_name_override': {'type': 'string'},
                    'setup': {'min_compat': '8.3', 'type': 'string'},
                    'severity': {'enum': ['low',
                                          'medium',
                                          'high',
                                          'critical'],
                                 'enumNames': [],
                                 'type': 'string'},
                    'severity_mapping': {'items': {'additionalProperties': False,
                                                   'properties': {'field': {'type': 'string'},
                                                                  'operator': {'enum': ['equals'],
                                                                               'type': 'string'},
                                                                  'severity': {'type': 'string'},
                                                                  'value': {'type': 'string'}},
                                                   'required': ['field'],
                                                   'type': 'object'},
                                         'type': 'array'},
                    'tags': {'items': {'type': 'string'}, 'type': 'array'},
                    'threat': {'items': {'additionalProperties': False,
                                         'properties': {'framework': {'enum': ['MITRE '
                                                                               'ATT&CK'],
                                                                      'type': 'string'},
                                                        'tactic': {'additionalProperties': False,
                                                                   'properties': {'id': {'type': 'string'},
                                                                                  'name': {'type': 'string'},
                                                                                  'reference': {'description': 'TacticURL',
                                                                                                'pattern': '^https://attack.mitre.org/tactics/TA[0-9]+/$',
                                                                                                'type': 'string'}},
                                                                   'required': ['id',
                                                                                'name',
                                                                                'reference'],
                                                                   'type': 'object'},
                                                        'technique': {'items': {'additionalProperties': False,
                                                                                'properties': {'id': {'type': 'string'},
                                                                                               'name': {'type': 'string'},
                                                                                               'reference': {'description': 'TechniqueURL',
                                                                                                             'pattern': '^https://attack.mitre.org/techniques/T[0-9]+/$',
                                                                                                             'type': 'string'},
                                                                                               'subtechnique': {'items': {'additionalProperties': False,
                                                                                                                          'properties': {'id': {'type': 'string'},
                                                                                                                                         'name': {'type': 'string'},
                                                                                                                                         'reference': {'description': 'SubTechniqueURL',
                                                                                                                                                       'pattern': '^https://attack.mitre.org/techniques/T[0-9]+/[0-9]+/$',
                                                                                                                                                       'type': 'string'}},
                                                                                                                          'required': ['id',
                                                                                                                                       'name',
                                                                                                                                       'reference'],
                                                                                                                          'type': 'object'},
                                                                                                                'type': 'array'}},
                                                                                'required': ['id',
                                                                                             'name',
                                                                                             'reference'],
                                                                                'type': 'object'},
                                                                      'type': 'array'}},
                                         'required': ['framework',
                                                      'tactic'],
                                         'type': 'object'},
                               'type': 'array'},
                    'throttle': {'type': 'string'},
                    'timeline_id': {'description': 'TimelineTemplateId',
                                    'enum': ['db366523-f1c6-4c1f-8731-6ce5ed9e5717',
                                             '91832785-286d-4ebe-b884-1a208d111a70',
                                             '76e52245-7519-4251-91ab-262fb1a1728c',
                                             '495ad7a7-316e-4544-8a0f-9c098daee76e',
                                             '4d4c0b59-ea83-483f-b8c1-8c360ee53c5c',
                                             'e70679c2-6cde-4510-9764-4823df18f7db',
                                             '300afc76-072d-4261-864d-4149714bf3f1',
                                             '3e47ef71-ebfc-4520-975c-cb27fc090799',
                                             '3e827bab-838a-469f-bd1e-5e19a2bff2fd',
                                             '4434b91a-94ca-4a89-83cb-a37cdc0532b7'],
                                    'enumNames': [],
                                    'type': 'string'},
                    'timeline_title': {'description': 'TimelineTemplateTitle',
                                       'enum': ['Generic Endpoint Timeline',
                                                'Generic Network Timeline',
                                                'Generic Process Timeline',
                                                'Generic Threat Match '
                                                'Timeline',
                                                'Comprehensive File '
                                                'Timeline',
                                                'Comprehensive Process '
                                                'Timeline',
                                                'Comprehensive Network '
                                                'Timeline',
                                                'Comprehensive Registry '
                                                'Timeline',
                                                'Alerts Involving a Single '
                                                'User Timeline',
                                                'Alerts Involving a Single '
                                                'Host Timeline'],
                                       'enumNames': [],
                                       'type': 'string'},
                    'timestamp_override': {'type': 'string'},
                    'to': {'type': 'string'},
                    'type': {'enum': ['new_terms'], 'type': 'string'}},
     'required': ['author',
                  'description',
                  'language',
                  'name',
                  'new_terms',
                  'query',
                  'risk_score',
                  'rule_id',
                  'severity',
                  'type'],
     'type': 'object'}

On instance:
    {'author': ['Nick Jones', 'Elastic'],
     'description': 'An adversary equipped with compromised credentials '
                    'may attempt to access the secrets in secrets manager '
                    'to steal certificates, credentials, or other '
                    'sensitive material.',
     'false_positives': ['Verify whether the user identity, user agent, '
                         'and/or hostname should be using GetSecretString '
                         'API for the specified SecretId. If known '
                         'behavior is causing false positives, it can be '
                         'exempted from the rule.'],
     'from': 'now-60m',
     'index': ['filebeat-*', 'logs-aws*'],
     'interval': '10m',
     'language': 'kuery',
     'license': 'Elastic License v2',
     'meta': {'creation_date': '2020/07/06',
              'integration': ['aws'],
              'maturity': 'production',
              'min_stack_comments': 'New fields added: required_fields, '
                                    'related_integrations, setup',
              'min_stack_version': '8.6.0',
              'updated_date': '2023/06/22'},
     'name': 'First Time Seen AWS Secret Value Accessed in Secrets Manager',
     'note': '## Triage and analysis\n'
             '\n'
             '### Investigating First Time Seen AWS Secret Value Accessed '
             'in Secrets Manager\n'
             '\n'
             'AWS Secrets Manager is a service that enables the '
             'replacement of hardcoded credentials in code, including '
             'passwords, with an API call to Secrets Manager to retrieve '
             'the secret programmatically.\n'
             '\n'
             'This rule looks for the retrieval of credentials using '
             '`GetSecretValue` action in Secrets Manager programmatically. '
             'This is a [New '
             'Terms](https://www.elastic.co/guide/en/security/master/rules-ui-create.html#create-new-terms-rule) '
             'rule indicating this is the first time a specific user '
             'identity has successfuly retrieved a secret value from '
             'Secrets Manager.\n'
             '\n'
             '#### Possible investigation steps\n'
             '\n'
             '- Identify the account and its role in the environment, and '
             'inspect the related policy.\n'
             '- Identify the applications that should use this account.\n'
             '- Investigate other alerts associated with the user account '
             'during the past 48 hours.\n'
             '- Investigate abnormal values in the `user_agent.original` '
             'field by comparing them with the intended and authorized '
             'usage and historical data. Suspicious user agent values '
             'include non-SDK, AWS CLI, custom user agents, etc.\n'
             '- Assess whether this behavior is prevalent in the '
             'environment by looking for similar occurrences involving '
             'other users.\n'
             '- Contact the account owner and confirm whether they are '
             'aware of this activity.\n'
             '- Considering the source IP address and geolocation of the '
             'user who issued the command:\n'
             '    - Do they look normal for the calling user?\n'
             '    - If the source is an EC2 IP address, is it associated '
             'with an EC2 instance in one of your accounts or is the '
             "source IP from an EC2 instance that's not under your "
             'control?\n'
             '    - If it is an authorized EC2 instance, is the activity '
             'associated with normal behavior for the instance role or '
             'roles? Are there any other alerts or signs of suspicious '
             'activity involving this instance?\n'
             '- Review IAM permission policies for the user identity and '
             'specific secrets accessed.\n'
             '- Examine the request parameters. These might indicate the '
             'source of the program or the nature of its tasks.\n'
             '- If you suspect the account has been compromised, scope '
             'potentially compromised assets by tracking servers, '
             'services, and data accessed by the account in the last 24 '
             'hours.\n'
             '\n'
             '### False positive analysis\n'
             '\n'
             '- False positives may occur due to the intended usage of the '
             'service. Tuning is needed in order to have higher '
             'confidence. Consider adding exceptions — preferably with a '
             'combination of user agent and IP address conditions.\n'
             '\n'
             '### Response and remediation\n'
             '\n'
             '- Initiate the incident response process based on the '
             'outcome of the triage.\n'
             '- Disable or limit the account during the investigation and '
             'response.\n'
             '- Identify the possible impact of the incident and '
             'prioritize accordingly; the following actions can help you '
             'gain context:\n'
             '    - Identify the account role in the cloud environment.\n'
             '    - Assess the criticality of affected services and '
             'servers.\n'
             '    - Work with your IT team to identify and minimize the '
             'impact on users.\n'
             '    - Identify if the attacker is moving laterally and '
             'compromising other accounts, servers, or services.\n'
             '    - Identify any regulatory or legal ramifications related '
             'to this activity.\n'
             '- Investigate credential exposure on systems compromised or '
             'used by the attacker to ensure all compromised accounts are '
             'identified. Rotate secrets or delete API keys as needed to '
             "revoke the attacker's access to the environment. Work with "
             'your IT teams to minimize the impact on business operations '
             'during these actions.\n'
             '- Check if unauthorized new users were created, remove '
             'unauthorized new accounts, and request password resets for '
             'other IAM users.\n'
             '- Consider enabling multi-factor authentication for users.\n'
             '- Review the permissions assigned to the implicated user to '
             'ensure that the least privilege principle is being '
             'followed.\n'
             '- Implement security best practices '
             '[outlined](https://aws.amazon.com/premiumsupport/knowledge-center/security-best-practices/) '
             'by AWS.\n'
             '- Take the actions needed to return affected systems, data, '
             'or services to their normal operational levels.\n'
             '- Identify the initial vector abused by the attacker and '
             'take action to prevent reinfection via the same vector.\n'
             '- Using the incident response data, update logging and audit '
             'policies to improve the mean time to detect (MTTD) and the '
             'mean time to respond (MTTR).',
     'query': 'event.dataset:aws.cloudtrail and '
              'event.provider:secretsmanager.amazonaws.com and\n'
              '    event.action:GetSecretValue and event.outcome:success '
              'and\n'
              '    not user_agent.name: ("Chrome" or "Firefox" or "Safari" '
              'or "Edge" or "Brave" or "Opera" or "aws-cli")\n',
     'references': ['https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html',
                    'http://detectioninthe.cloud/credential_access/access_secret_in_secrets_manager/'],
     'risk_score': 47,
     'rule_id': 'ce25c342-4569-4bda-badc-65be27ca221b',
     'setup': 'The AWS Fleet integration, Filebeat module, or similarly '
              'structured data is required to be compatible with this '
              'rule.',
     'severity': 'medium',
     'tags': ['Domain: Cloud',
              'Data Source: AWS',
              'Data Source: Amazon Web Services',
              'Tactic: Credential Access',
              'Resources: Investigation Guide'],
     'threat': [{'framework': 'MITRE ATT&CK',
                 'tactic': {'id': 'TA0006',
                            'name': 'Credential Access',
                            'reference': 'https://attack.mitre.org/tactics/TA0006/'},
                 'technique': [{'id': 'T1528',
                                'name': 'Steal Application Access Token',
                                'reference': 'https://attack.mitre.org/techniques/T1528/'}]}],
     'timestamp_override': 'event.ingested',
     'type': 'new_terms'}

Passing

-m detection_rules kibana upload-rule -id a00681e3-9ed6-447c-ab2c-be648821c622 -r 
Loaded config file: /Users/stryker/workspace/Elastic/detection-rules/.detection-rules-cfg.json

█▀▀▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄   ▄      █▀▀▄ ▄  ▄ ▄   ▄▄▄ ▄▄▄
█  █ █▄▄  █  █▄▄ █    █   █  █ █ █▀▄ █      █▄▄▀ █  █ █   █▄▄ █▄▄
█▄▄▀ █▄▄  █  █▄▄ █▄▄  █  ▄█▄ █▄█ █ ▀▄█      █ ▀▄ █▄▄█ █▄▄ █▄▄ ▄▄█

Successful uploads:
  - fd8c82cf-cb0d-4a99-8774-b827776aafd3

@Mikaayenson Mikaayenson added bug Something isn't working cli command line tooling python Internal python for the repository Area: DED labels Jul 31, 2023
@Mikaayenson Mikaayenson self-assigned this Jul 31, 2023
@Mikaayenson Mikaayenson linked an issue Jul 31, 2023 that may be closed by this pull request
@botelastic botelastic bot added the schema label Jul 31, 2023
@Mikaayenson Mikaayenson changed the title [Bug] Strip NON Public Fields Prior to Uploading Rules [Bug] Strip Non-Public Fields Prior to Uploading Rules Jul 31, 2023
meta["original"] = dict(id=rule.id, **rule.contents.metadata.to_dict())
payload["rule_id"] = str(uuid4())
payload = downgrade(payload, target_version)
rule_dict = rule.contents.to_dict()["rule"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, exactly what we discussed. to_api_format() would result in a dict AFTER the transform method was called, thus being non-compatible with the frozen jsonschema. 🔥

Comment on lines +1316 to +1320
meta = rule_dict.pop("meta")
rule_contents = TOMLRuleContents.from_dict({"rule": rule_dict, "metadata": meta,
"transform": rule.contents.transform})
payload = rule_contents.to_api_format()
payload = strip_non_public_fields(min_stack_version, payload)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 - downgrade rule, which goes through jsonschema with the to_dict() format. Then pop the meta to remove it from being in the rule object. Passing contents to TOMLRuleContents and then calling to_api_format() to make it compatible with Kibana API. Then stripping the non-public fields as discussed. This looks NICE!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. This approach seemed cleaner than passing the rule object and updating all the function definitions.

Comment on lines +32 to +35
NON_PUBLIC_FIELDS = {
"related_integrations": (Version.parse('8.3.0'), None),
"required_fields": (Version.parse('8.3.0'), None),
"setup": (Version.parse('8.3.0'), None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this in rule.py as well. Will we eventually be moving everything to non_public_fields or are we keeping build time and non-public fields separate in terms of functionality they are referenced for?

Copy link
Contributor Author

@Mikaayenson Mikaayenson Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Originally this was scoped just for 2931. Now that 2930 is in this as well, the overlap of BUILD_FIELD_VERSIONS seems very duplicative. The only caveat is that I just remembered that setup should probably not be added in the NON_PUBLIC_FIELDS object.

Even though they are the same today, the idea was that they may diverge in the future. It may be safer to keep them separate for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to keep them separate. While it is duplicative, it may be more difficult to separate them later should it be necessary. 👍

Copy link
Contributor

@terrancedejesus terrancedejesus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments and questions added. Looks good to merge as-is!

Copy link
Contributor

@eric-forte-elastic eric-forte-elastic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟩 PR test steps passed.

Testing output

> python -m detection_rules kibana --provider-name cloud-basic --kibana-url *** upload-rule -id a00681e3-9ed6-447c-ab2c-be648821c622 -r

█▀▀▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄   ▄      █▀▀▄ ▄  ▄ ▄   ▄▄▄ ▄▄▄
█  █ █▄▄  █  █▄▄ █    █   █  █ █ █▀▄ █      █▄▄▀ █  █ █   █▄▄ █▄▄
█▄▄▀ █▄▄  █  █▄▄ █▄▄  █  ▄█▄ █▄█ █ ▀▄█      █ ▀▄ █▄▄█ █▄▄ █▄▄ ▄▄█

kibana_user: eric.forte
kibana_password: 
Successful uploads:
  - 163cd46c-316a-469b-872c-6cbe26e9f107

Manual review, LGTM 👍

@Mikaayenson Mikaayenson merged commit 3f9e7ac into main Aug 2, 2023
@Mikaayenson Mikaayenson deleted the 2931-bug-non-public-fields-not-stripped-on-future-builds branch August 2, 2023 17:38
protectionsmachine pushed a commit that referenced this pull request Aug 2, 2023
protectionsmachine pushed a commit that referenced this pull request Aug 2, 2023
protectionsmachine pushed a commit that referenced this pull request Aug 2, 2023
protectionsmachine pushed a commit that referenced this pull request Aug 2, 2023
protectionsmachine pushed a commit that referenced this pull request Aug 2, 2023
protectionsmachine pushed a commit that referenced this pull request Aug 2, 2023
protectionsmachine pushed a commit that referenced this pull request Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport: auto bug Something isn't working cli command line tooling python Internal python for the repository schema
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug] Non Public Fields Not Stripped on Future Builds [Bug] Review New Terms Schema and Transform Method Regarding JSONSchema
3 participants