From cd4034746a957520acad719929678029d6a2a36f Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Wed, 16 Sep 2026 20:34:19 -0400 Subject: [PATCH 1/3] fix: align AWS filter and rule contracts --- filters/audits/aws.md | 40 ++++++ filters/aws/aws.yml | 26 +++- .../alerts/testdata/filter-contracts/aws.json | 118 ++++++++++++++++++ 3 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 filters/audits/aws.md create mode 100644 plugins/alerts/testdata/filter-contracts/aws.json diff --git a/filters/audits/aws.md b/filters/audits/aws.md new file mode 100644 index 000000000..62ec734fb --- /dev/null +++ b/filters/audits/aws.md @@ -0,0 +1,40 @@ +# AWS normalization and rule review + +Promote actor, valid source IP and event time while retaining vendor fields read by rules. + +This draft targets UTMStack `v11`. It contains 1 filter changes +and 0 rule changes for this technology only. Review covered +1 filter configurations and 73 matching shipped rule files. +Unchanged rules are listed in the regression manifest; they are not duplicated in the diff. + +## Contract and validation + +- Compared exact standard names/types with go-sdk v1.1.31 and the supplied UTMStack dictionaries. +- Checked documented pipeline ordering, rename/move behavior, open vendor log fields, + event-side versus alert-side fields, and surviving fields used by affected rule predicates/history/grouping. +- Strict SDK configuration decoding and actual CEL compilation pass for this scope. +- 2 synthetic normalization cases pass, including SDK Event conversion and any + trigger predicate assertions recorded in the manifest. +- The scoped alerts module tests and `git diff --check` pass with the shared contract runner applied. + +The shared alert-contract PR supplies the reusable Go runner for the manifest in +`plugins/alerts/testdata/filter-contracts/aws.json`. Apply that support before running `go test ./...` in `plugins/alerts`. + +The model starts from synthetic extraction results. It does not run complex grok, +JSON/KV/XML/CSV extraction, time conversion, dynamic plugins, historical OpenSearch +queries, or the closed EventProcessor. Raw vendor logs and resulting alerts must +still be checked in staging before rollout. No customer false-positive reduction +has been measured and no production rollout is included. + + + +## References + +- [SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) +- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) +- [Standard event schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema) +- [Rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) + +`afterEvents`, empty noncapturing grok names, supported numeric strings, and custom +`log.*` fields are accepted. Existing textual protocol casing and vendor action names +are preserved unless a concrete consumer mismatch requires correction. diff --git a/filters/aws/aws.yml b/filters/aws/aws.yml index 811ad01b8..bc14b5700 100644 --- a/filters/aws/aws.yml +++ b/filters/aws/aws.yml @@ -234,12 +234,32 @@ pipeline: function: string params: key: actionResult - value: "failed" - where: 'exists("log.errorCode") && !equals("log.errorCode", "AccessDenied")' + value: failure + where: exists("log.errorCode") && !equals("log.errorCode", "AccessDenied") - delete: fields: - log.requestParameters - log.responseElements - log.userIdentity - - log.additionalEventData \ No newline at end of file + - log.additionalEventData + + # Promote standard fields while retaining vendor fields used by rules. + - grok: + source: log.sourceIPAddress + patterns: + - fieldName: origin.ip + pattern: '{{.greedy}}' + where: inCIDR("log.sourceIPAddress","0.0.0.0/0") || inCIDR("log.sourceIPAddress","::/0") + - grok: + source: log.userIdentityArn + patterns: + - fieldName: origin.user + pattern: '{{.greedy}}' + where: exists("log.userIdentityArn") && !exists("origin.user") + - grok: + source: log.eventTime + patterns: + - fieldName: deviceTime + pattern: '{{.greedy}}' + where: exists("log.eventTime") && !exists("deviceTime") diff --git a/plugins/alerts/testdata/filter-contracts/aws.json b/plugins/alerts/testdata/filter-contracts/aws.json new file mode 100644 index 000000000..89bdd8992 --- /dev/null +++ b/plugins/alerts/testdata/filter-contracts/aws.json @@ -0,0 +1,118 @@ +{ + "technology": "AWS", + "filters": [ + "filters/aws/aws.yml" + ], + "rules": [ + "rules/cloud/aws/aws/aws_backup_deletion.yml", + "rules/cloud/aws/aws/aws_config_service_disabled.yml", + "rules/cloud/aws/aws/aws_console_getsignintoken_abuse.yml", + "rules/cloud/aws/aws/aws_ec2_startup_script_modification.yml", + "rules/cloud/aws/aws/aws_ecs_credential_theft.yml", + "rules/cloud/aws/aws/aws_glue_privilege_escalation.yml", + "rules/cloud/aws/aws/aws_golden_saml_attack.yml", + "rules/cloud/aws/aws/aws_iam_login_profile_modification.yml", + "rules/cloud/aws/aws/aws_kms_key_material_import.yml", + "rules/cloud/aws/aws/aws_rds_public_restore.yml", + "rules/cloud/aws/aws/aws_s3_versioning_disabled.yml", + "rules/cloud/aws/aws/aws_securityhub_finding_evasion.yml", + "rules/cloud/aws/aws/aws_snapshot_exfiltration.yml", + "rules/cloud/aws/aws/aws_ssm_sendcommand_abuse.yml", + "rules/cloud/aws/aws/aws_sso_idp_change.yml", + "rules/cloud/aws/aws/aws_sso_suspicious_activities.yml", + "rules/cloud/aws/aws/aws_trufflehog_scanning.yml", + "rules/cloud/aws/aws/cloudformation_stack_deletion.yml", + "rules/cloud/aws/aws/cloudtrail_event_selector_manipulation.yml", + "rules/cloud/aws/aws/cloudtrail_logging_disabled.yml", + "rules/cloud/aws/aws/console_login_impossible_travel.yml", + "rules/cloud/aws/aws/cross_account_access_anomalies.yml", + "rules/cloud/aws/aws/ebs_snapshot_sharing_violations.yml", + "rules/cloud/aws/aws/ec2_cryptomining_detection.yml", + "rules/cloud/aws/aws/ec2_metadata_abuse.yml", + "rules/cloud/aws/aws/ecs_eks_container_abuse.yml", + "rules/cloud/aws/aws/guardduty_high_severity_findings.yml", + "rules/cloud/aws/aws/iam_backdoor_creation_attempts.yml", + "rules/cloud/aws/aws/iam_privilege_escalation_paths.yml", + "rules/cloud/aws/aws/lambda_privilege_escalation.yml", + "rules/cloud/aws/aws/lambda_url_backdoor.yml", + "rules/cloud/aws/aws/mass_resource_deletion.yml", + "rules/cloud/aws/aws/rds_security_group_changes.yml", + "rules/cloud/aws/aws/rds_snapshot_exfiltration.yml", + "rules/cloud/aws/aws/root_account_usage_without_mfa.yml", + "rules/cloud/aws/aws/route53_dns_hijacking.yml", + "rules/cloud/aws/aws/s3_bucket_public_exposure.yml", + "rules/cloud/aws/aws/s3_bulk_data_exfiltration.yml", + "rules/cloud/aws/aws/secrets_manager_suspicious_access.yml", + "rules/cloud/aws/aws/security_group_modifications.yml", + "rules/cloud/aws/aws/ssm_session_abuse.yml", + "rules/cloud/aws/aws/sts_token_abuse.yml", + "rules/cloud/aws/aws/unusual_api_call_patterns.yml", + "rules/cloud/aws/aws/vpc_flow_log_anomalies.yml", + "rules/cloud/aws/aws/waf_shield_rule_modifications.yml", + "rules/cloud/aws/credential_access_aws_iam_assume_role_brute_force.yml", + "rules/cloud/aws/credential_access_root_console_failure_brute_force.yml", + "rules/cloud/aws/defense_evasion_cloudtrail_logging_deleted.yml", + "rules/cloud/aws/defense_evasion_cloudtrail_logging_suspended.yml", + "rules/cloud/aws/defense_evasion_config_service_rule_deletion.yml", + "rules/cloud/aws/defense_evasion_configuration_recorder_stopped.yml", + "rules/cloud/aws/defense_evasion_ec2_flow_log_deletion.yml", + "rules/cloud/aws/defense_evasion_guardduty_detector_deletion.yml", + "rules/cloud/aws/defense_evasion_s3_bucket_configuration_deletion.yml", + "rules/cloud/aws/defense_evasion_waf_acl_deletion.yml", + "rules/cloud/aws/defense_evasion_waf_rule_or_rule_group_deletion.yml", + "rules/cloud/aws/exfiltration_ec2_full_network_packet_capture_detected.yml", + "rules/cloud/aws/exfiltration_ec2_snapshot_change_activity.yml", + "rules/cloud/aws/exfiltration_ec2_vm_export_failure.yml", + "rules/cloud/aws/exfiltration_rds_snapshot_export.yml", + "rules/cloud/aws/impact_cloudtrail_logging_updated.yml", + "rules/cloud/aws/impact_cloudwatch_log_group_deletion.yml", + "rules/cloud/aws/impact_cloudwatch_log_stream_deletion.yml", + "rules/cloud/aws/impact_ec2_disable_ebs_encryption.yml", + "rules/cloud/aws/impact_iam_deactivate_mfa_device.yml", + "rules/cloud/aws/impact_rds_cluster_deletion.yml", + "rules/cloud/aws/initial_access_console_login_root.yml", + "rules/cloud/aws/initial_access_password_recovery.yml", + "rules/cloud/aws/initial_access_via_system_manager.yml", + "rules/cloud/aws/persistence_route_53_domain_transfer_lock_disabled.yml", + "rules/cloud/aws/persistence_route_53_domain_transferred_to_another_account.yml", + "rules/cloud/aws/privilege_escalation_root_login_without_mfa.yml", + "rules/cloud/aws/privilege_escalation_updateassumerolepolicy.yml" + ], + "fixtures": [ + { + "name": "AWS principal/source/time promoted", + "filter": "aws/aws.yml", + "input": { + "log": { + "sourceIPAddress": "198.51.100.10", + "eventTime": "2026-09-16T12:00:00Z", + "userIdentity": { + "arn": "arn:aws:iam::123456789012:user/alice" + } + } + }, + "expected": { + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/alice", + "deviceTime": "2026-09-16T12:00:00Z", + "log.userIdentityArn": "arn:aws:iam::123456789012:user/alice" + }, + "absent": [], + "rules": {} + }, + { + "name": "AWS service name is not IP", + "filter": "aws/aws.yml", + "input": { + "log": { + "sourceIPAddress": "cloudtrail.amazonaws.com" + } + }, + "expected": {}, + "absent": [ + "origin.ip" + ], + "rules": {} + } + ] +} From a8f7d832b2f98cea3aa5c01efcb791774c627841 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Thu, 17 Sep 2026 19:51:26 -0400 Subject: [PATCH 2/3] Repair AWS CloudTrail normalization and rule consumer contracts --- filters/audits/aws.md | 175 +- filters/aws/aws.yml | 826 ++++-- plugins/alerts/aws_contract_test.go | 490 ++++ plugins/alerts/aws_history_test.go | 328 +++ plugins/alerts/testdata/aws_raw.json | 2483 +++++++++++++++++ .../alerts/testdata/filter-contracts/aws.json | 112 +- rules/cloud/aws/aws/aws_backup_deletion.yml | 23 +- .../aws/aws/aws_config_service_disabled.yml | 23 +- .../aws/aws_console_getsignintoken_abuse.yml | 23 +- .../aws_ec2_startup_script_modification.yml | 27 +- .../aws/aws/aws_ecs_credential_theft.yml | 76 +- .../aws/aws/aws_glue_privilege_escalation.yml | 23 +- .../cloud/aws/aws/aws_golden_saml_attack.yml | 80 +- .../aws_iam_login_profile_modification.yml | 23 +- .../aws/aws/aws_kms_key_material_import.yml | 34 +- .../cloud/aws/aws/aws_rds_public_restore.yml | 39 +- .../aws/aws/aws_s3_versioning_disabled.yml | 28 +- .../aws/aws_securityhub_finding_evasion.yml | 67 +- .../aws/aws/aws_snapshot_exfiltration.yml | 45 +- .../aws/aws/aws_ssm_sendcommand_abuse.yml | 65 +- rules/cloud/aws/aws/aws_sso_idp_change.yml | 23 +- .../aws/aws/aws_sso_suspicious_activities.yml | 78 +- .../cloud/aws/aws/aws_trufflehog_scanning.yml | 23 +- .../aws/aws/cloudformation_stack_deletion.yml | 68 +- ...cloudtrail_event_selector_manipulation.yml | 23 +- .../aws/aws/cloudtrail_logging_disabled.yml | 25 +- .../aws/console_login_impossible_travel.yml | 88 +- .../aws/cross_account_access_anomalies.yml | 73 +- .../aws/ebs_snapshot_sharing_violations.yml | 49 +- .../aws/aws/ec2_cryptomining_detection.yml | 41 +- rules/cloud/aws/aws/ec2_metadata_abuse.yml | 42 +- .../cloud/aws/aws/ecs_eks_container_abuse.yml | 37 +- .../aws/guardduty_high_severity_findings.yml | 36 +- .../aws/iam_backdoor_creation_attempts.yml | 63 +- .../aws/iam_privilege_escalation_paths.yml | 67 +- .../aws/aws/lambda_privilege_escalation.yml | 83 +- rules/cloud/aws/aws/lambda_url_backdoor.yml | 27 +- .../cloud/aws/aws/mass_resource_deletion.yml | 65 +- .../aws/aws/rds_security_group_changes.yml | 41 +- .../aws/aws/rds_snapshot_exfiltration.yml | 30 +- .../aws/root_account_usage_without_mfa.yml | 29 +- rules/cloud/aws/aws/route53_dns_hijacking.yml | 68 +- .../aws/aws/s3_bucket_public_exposure.yml | 55 +- .../aws/aws/s3_bulk_data_exfiltration.yml | 65 +- .../aws/secrets_manager_suspicious_access.yml | 100 +- .../aws/aws/security_group_modifications.yml | 76 +- rules/cloud/aws/aws/ssm_session_abuse.yml | 65 +- rules/cloud/aws/aws/sts_token_abuse.yml | 67 +- .../aws/aws/unusual_api_call_patterns.yml | 63 +- .../cloud/aws/aws/vpc_flow_log_anomalies.yml | 83 +- .../aws/aws/waf_shield_rule_modifications.yml | 24 +- ...access_aws_iam_assume_role_brute_force.yml | 76 +- ...ccess_root_console_failure_brute_force.yml | 71 +- ...nse_evasion_cloudtrail_logging_deleted.yml | 34 +- ...e_evasion_cloudtrail_logging_suspended.yml | 35 +- ...e_evasion_config_service_rule_deletion.yml | 34 +- ...evasion_configuration_recorder_stopped.yml | 34 +- .../defense_evasion_ec2_flow_log_deletion.yml | 35 +- ...se_evasion_guardduty_detector_deletion.yml | 35 +- ...asion_s3_bucket_configuration_deletion.yml | 40 +- .../aws/defense_evasion_waf_acl_deletion.yml | 34 +- ...vasion_waf_rule_or_rule_group_deletion.yml | 35 +- ...2_full_network_packet_capture_detected.yml | 39 +- ...iltration_ec2_snapshot_change_activity.yml | 35 +- .../exfiltration_ec2_vm_export_failure.yml | 36 +- .../aws/exfiltration_rds_snapshot_export.yml | 31 +- .../aws/impact_cloudtrail_logging_updated.yml | 38 +- .../impact_cloudwatch_log_group_deletion.yml | 39 +- .../impact_cloudwatch_log_stream_deletion.yml | 40 +- .../aws/impact_ec2_disable_ebs_encryption.yml | 37 +- .../aws/impact_iam_deactivate_mfa_device.yml | 37 +- .../cloud/aws/impact_rds_cluster_deletion.yml | 40 +- .../aws/initial_access_console_login_root.yml | 38 +- .../aws/initial_access_password_recovery.yml | 34 +- .../aws/initial_access_via_system_manager.yml | 34 +- ...route_53_domain_transfer_lock_disabled.yml | 37 +- ..._domain_transferred_to_another_account.yml | 35 +- ...lege_escalation_root_login_without_mfa.yml | 35 +- ...lege_escalation_updateassumerolepolicy.yml | 32 +- 79 files changed, 6061 insertions(+), 1686 deletions(-) create mode 100644 plugins/alerts/aws_contract_test.go create mode 100644 plugins/alerts/aws_history_test.go create mode 100644 plugins/alerts/testdata/aws_raw.json diff --git a/filters/audits/aws.md b/filters/audits/aws.md index 62ec734fb..c698fde71 100644 --- a/filters/audits/aws.md +++ b/filters/audits/aws.md @@ -1,40 +1,163 @@ -# AWS normalization and rule review +# AWS filter and correlation review — 2026-09-17 -Promote actor, valid source IP and event time while retaining vendor fields read by rules. +Replacement for historical PR #2596 (`cd4034746a957520acad719929678029d6a2a36f`). +This review changes the AWS filter and all 73 consumers discovered recursively in +both `rules/cloud/aws/` and `rules/cloud/aws/aws/`. It is a draft for review, not a +production deployment. The shared alert grouping support is in draft #2627. -This draft targets UTMStack `v11`. It contains 1 filter changes -and 0 rule changes for this technology only. Review covered -1 filter configurations and 73 matching shipped rule files. -Unchanged rules are listed in the regression manifest; they are not duplicated in the diff. +## Evidence and limits -## Contract and validation +Fresh read-only retained-index counts returned **zero AWS records on 30 reachable +v11 instances**. One other instance was unavailable. No live AWS filter output, +AWS customer document ID, resulting alert, or false-positive reduction is claimed. -- Compared exact standard names/types with go-sdk v1.1.31 and the supplied UTMStack dictionaries. -- Checked documented pipeline ordering, rename/move behavior, open vendor log fields, - event-side versus alert-side fields, and surviving fields used by affected rule predicates/history/grouping. -- Strict SDK configuration decoding and actual CEL compilation pass for this scope. -- 2 synthetic normalization cases pass, including SDK Event conversion and any - trigger predicate assertions recorded in the manifest. -- The scoped alerts module tests and `git diff --check` pass with the shared contract runner applied. +The contract is go-sdk **v1.1.31**, pinned by `plugins/alerts/go.mod`; its protobuf +and official wiki take precedence over local dictionaries. The reviewed wiki +revision is `c18b54bd5ea5a34abb0e690458d73f89835edd29`. The AWS collector forwards +individual CloudWatch `GetLogEvents` messages unchanged. It does not unwrap S3 +CloudTrail archive `Records` arrays. This filter handles individual CloudTrail +JSON records and explicitly identified GuardDuty EventBridge finding envelopes; +other AWS message formats are not silently treated as CloudTrail. -The shared alert-contract PR supplies the reusable Go runner for the manifest in -`plugins/alerts/testdata/filter-contracts/aws.json`. Apply that support before running `go test ./...` in `plugins/alerts`. +The private documentation replay contains 31 complete examples extracted from +AWS's record, sign-in, log-file and Route 53 documentation. Some are illustrative +examples with placeholder values. Additional official API references establish +operation semantics; they do not prove every service's deployed CloudTrail +request-parameter serialization. Nested variants in the synthetic fixtures must +still be checked against actual customer records when AWS telemetry is available. -The model starts from synthetic extraction results. It does not run complex grok, -JSON/KV/XML/CSV extraction, time conversion, dynamic plugins, historical OpenSearch -queries, or the closed EventProcessor. Raw vendor logs and resulting alerts must -still be checked in staging before rollout. No customer false-positive reduction -has been measured and no production rollout is included. +## Producer and consumer corrections +- Retain `userIdentity`, `requestParameters`, `responseElements`, + `additionalEventData` and `tlsDetails`. The previous filter moved selected + children and deleted their parents, although many rules consumed those parents. + Existing flattened aliases remain for compatibility. A second JSON pass restores + the original sanitized structures after the alias moves; this has a processing + and storage cost that has not been measured with the closed EventProcessor. +- Promote event name, time, caller ARN/user, literal source IP, service hostname and + target AWS service domain into supported standard fields. AWS service DNS names + and `AWS Internal/#` are not IP addresses. Unspecified IPv4/IPv6 addresses and + non-string user values are not promoted. Geolocation receives only a usable IP. + AWS accounts, reporting resources and GuardDuty findings are not invented actors. +- Derive `failure` from top-level or nested errors and explicit failed sign-in + results, with recognized authorization errors classified `denied`. Error-message- + only console failures cannot become `success`. Successful API requests normally + omit `errorCode`; SDK `equals(path, "")` does not match an absent field. Rules now + consume the derived outcome. API success means request success, not completion + of an asynchronous export, deletion, instance launch or command execution. +- Preserve `Root`, `MFAUsed: Yes/No` and session MFA fields. Missing MFA evidence is + not equivalent to an explicit negative value. Root login success, root login + failure and successful root login without MFA have distinct predicates. +- Replace string searches on JSON objects with field, boolean and nested-array + queries. SDK `contains` only handles string fields. Covered consumers include + startup data, RDS public restore, S3 versioning/ACL/block controls, snapshot + sharing, EC2 metadata, ECS task definitions and security-group permissions. + SDK field sanitization means `x-amz-acl` is read as `xamzacl` after JSON parsing. +- Restrict exposure predicates to permission additions or relaxed controls; + removing snapshot or security-group permissions does not establish exposure. + Correct cross-account ARN comparison to exact account boundaries and quote the + field paths passed to `safe`. Use the Route 53 Domains service namespace and its + documented event-name capitalization variants. +- Consume GuardDuty EventBridge `detail.severity`, not the severity of an unrelated + GuardDuty management API call. High/critical findings use the documented 7–10 + range and group by collector/account/region/finding ID. Ingestion of that feed is + not proven by this review. No finding action or attacker IP is fabricated. +- Correct claims that exceeded the events: task management calls do not prove ECS + credential endpoint access; rejected trust-policy updates are not password brute + force; a SAML provider-change sequence does not prove forged SAML; country changes + do not prove impossible travel; an instance type does not prove cryptomining. + Existing administrative activity heuristics still require operational tuning. + Correct unrelated ATT&CK labels for secret-store access, cloud discovery and IAM + grants; omit specific techniques where the broad signal cannot establish one. +## Historical correlation and grouping -## References +All 22 history-bearing rules now restrict the population to the relevant filter- +produced candidate, collector and namespaced account. Original IP-based populations +remain IP-scoped; actor-based populations use a namespaced ARN/principal/IP identity. +Missing required identities prevent the trigger instead of producing a broad query. +Input-supplied candidate and identity markers are cleared before derivation. + +Existing thresholds and windows are retained, including 3/30m and 100/15m and windows up to 24h, +with these explicit sequence corrections: + +- SAML role assumption requires a successful CreateSAMLProvider/UpdateSAMLProvider + in the same account/collector within 24h. The administrator can differ. Ordinary + account activity and the login itself cannot satisfy that prerequisite. This is + account-level context, not proof that the changed provider issued the assertion. +- Secrets Manager uses **10 GetSecretValue OR 5 BatchGetSecretValue within 10m**; + the earlier two top-level blocks required both populations. Mixed counts below + both thresholds do not qualify. +- Different-country login history also requires successful console authentication + and a country value. Failed logins and missing geolocation cannot satisfy it. +- Administrative policy-attachment history counts the same reviewed role/user + attachment population as its trigger, rather than unrelated IAM requests. + +Grouping/deduplication retain the intended field type and remain mutually exclusive. +CloudTrail groups include collector and account scope; the snapshot identifier is +read from the request. Indexed `lastEvent.*` names remain valid and depend on shared +PR #2627 to resolve values from the alert's wire `events[]`. This review does not +claim to have run live grouping or alert creation. + +## Validation + +- **190 synthetic raw JSON cases**, each evaluated against all **73 predicates**, + with positive and negative coverage for every rule. Cases cover omissions, nested + errors, denials, MFA states, arrays, removal-only changes, exact ARN boundaries, + unknown records, marker spoofing, invalid identity/numeric values, DNS callers, + unspecified IPs, GuardDuty boundaries and archive wrappers. +- **31 official AWS examples** replayed privately with independently specified + field, outcome and predicate assertions; no evaluation errors. They include + error-message-only failed root logins and successful calls with absent errorCode. +- **22 actual SDK history suites**, against an isolated loopback OpenSearch mock, + assert exact query terms, account/collector/actor/IP scopes, threshold/window + boundaries, missing placeholders, unrelated populations and sequence/OR behavior. +- Strict SDK YAML decoding, field-name/type checks, final Event serialization and + the shared contract suite pass: **275 pass records, zero skips/failures** in the + source run with official examples supplied. The history subprocess asserts its + 22 cases internally; the outer pass count is not a count of live detections. + +`aws_contract_test.go` models explicit YAML JSON sanitization, grok, rename, add and +delete operations and uses the real SDK CEL evaluator and Event conversion. +`aws_history_test.go` uses the SDK SearchRequest implementation with a mock backend. +Neither executes the closed EventProcessor, external geolocation service, real +OpenSearch mappings nor production alerts. Country tests explicitly supply mocked +geolocation. Without the private official example file, that test reports a skip. + +Run the source suite with the shared test support from #2627, then run the combined +source overlay before rollout. The filter and rules must ship together. Existing +indexed events do not acquire retained nested objects or candidate markers; allow +up to **24 hours** of new history before the longest rules reach full coverage. +Review dashboards/custom searches for changed standard outcomes, corrected aliases, +new nested objects, group scope and revised titles. Vendor action names are retained. + +## Explicit unresolved mapping + +The legacy `bytesTransferredIn -> origin.bytesReceived` and +`bytesTransferredOut -> origin.bytesSent` directions are **not verified**. The +fetched references and retained telemetry did not establish their viewpoint, so +this change does not reverse them speculatively. Finite nonnegative numeric guards +prevent invalid values entering the SDK double fields, and original vendor byte +fields are retained. None of these 73 AWS rules reads the byte fields. Do not use +this review as validation of AWS byte-based dashboards or external rules. + +## Primary references - [SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto) - [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference) - [Standard event schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema) -- [Rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) +- [Rules and correlation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules) +- [CloudTrail record fields](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html) +- [CloudTrail identity](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html) +- [Console sign-in examples](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-aws-console-sign-in-events.html) +- [CloudTrail log examples](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-log-file-examples.html) +- [Route 53 CloudTrail](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/logging-using-cloudtrail.html) +- [S3 CloudTrail event names](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cloudtrail-logging-s3-info.html) +- [IAM trust-policy update errors](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateAssumeRolePolicy.html) +- [EC2 snapshot permission changes](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifySnapshotAttribute.html) +- [GuardDuty EventBridge shape](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings_eventbridge.html) +- [GuardDuty severity ranges](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings-severity.html) -`afterEvents`, empty noncapturing grok names, supported numeric strings, and custom -`log.*` fields are accepted. Existing textual protocol casing and vendor action names -are preserved unless a concrete consumer mismatch requires correction. +Legacy third-party rule references are retained as contextual citations, not +verified format evidence. Vendor evidence in this review was fetched only from +allowlisted official AWS hosts. No customer payloads are committed. diff --git a/filters/aws/aws.yml b/filters/aws/aws.yml index bc14b5700..8608ab49d 100644 --- a/filters/aws/aws.yml +++ b/filters/aws/aws.yml @@ -1,265 +1,563 @@ -# AWS filter, version 1.0.2 - +# AWS filter v1.1.0: CloudTrail JSON and identified GuardDuty EventBridge findings. +# Original vendor objects and legacy aliases are retained. API success is request success. +# Correlation candidates are generated here, never trusted from input. pipeline: - - dataTypes: - - aws - steps: - - json: - source: raw - - - rename: - from: - - log.userIdentity.accessKeyId - to: log.userIdentityAccessKeyId - - - rename: - from: - - log.userIdentity.accountId - to: log.userIdentityAccountId - - - rename: - from: - - log.userIdentity.arn - to: log.userIdentityArn - - - rename: - from: - - log.userIdentity.invokedBy - to: log.userIdentityInvokedBy - - - rename: - from: - - log.userIdentity.principalId - to: log.userIdentityPrincipalId - - - rename: - from: - - log.userIdentity.sessionContext.attributes.creationDate - to: log.userIdentitySessionContextAttributesCreationDate - - - rename: - from: - - log.userIdentity.sessionContext.attributes.mfaAuthenticated - to: log.userIdentitySessionContextAttributesMfaAuthenticated - - - rename: - from: - - log.userIdentity.sessionContext.sessionIssuer.accountId - to: log.userIdentitySessionContextSessionIssuerAccountId - - - rename: - from: - - log.userIdentity.sessionContext.sessionIssuer.arn - to: log.userIdentitySessionContextSessionIssuerArn - - - rename: - from: - - log.userIdentity.sessionContext.sessionIssuer.principalId - to: log.userIdentitySessionContextSessionIssuerPrincipalId - - - rename: - from: - - log.userIdentity.sessionContext.sessionIssuer.type - to: log.userIdentitySessionContextSessionIssuerType - - - rename: - from: - - log.userIdentity.sessionContext.sessionIssuer.userName - to: log.userIdentitySessionContextSessionIssuerUserName - - - rename: - from: - - log.userIdentity.sessionContext.webIdFederationData - to: log.userIdentitySessionContextWebIdFederationData - - - rename: - from: - - log.userIdentity.type - to: log.userIdentityType - - - rename: - from: - - log.requestParameters.durationSeconds - to: log.requestParametersDurationSeconds - - - rename: - from: - - log.requestParameters.roleArn - to: log.requestParametersRoleArn - - - rename: - from: - - log.requestParameters.roleSessionName - to: log.requestParametersRoleSessionName - - - rename: - from: - - log.responseElements.assumedRoleUser.arn - to: log.responseElementsAssumedRoleUserArn - - - rename: - from: - - log.responseElements.assumedRoleUser.assumedRoleId - to: log.responseElementsAssumedRoleUserAssumedRoleId - - - rename: - from: - - log.responseElements.credentials.accessKeyId - to: log.responseElementsCredentialsAccessKeyId - - - rename: - from: - - log.responseElements.credentials.expiration - to: log.responseElementsCredentialsExpiration - - - rename: - from: - - log.responseElements.credentials.sessionToken - to: log.responseElementsCredentialsSessionToken - - - rename: - from: - - log.requestParameters.Host - to: log.requestParametersHost - - - rename: - from: - - log.requestParameters.acl - to: log.requestParametersAcl - - - rename: - from: - - log.requestParameters.bucketName - to: log.userIdentityAccessrequestParametersBucketNamesKeyId - - - rename: - from: - - log.additionalEventData.SignatureVersion - to: log.additionalEventDataSignatureVersion - - - rename: - from: - - log.additionalEventData.AuthenticationMethod - to: log.additionalEventDataAuthenticationMethod - - - rename: - from: - - log.additionalEventData.CipherSuite - to: log.additionalEventDataCipherSuite - - - rename: - from: - - log.additionalEventData.bytesTransferredIn - to: origin.bytesReceived - - - rename: - from: - - log.additionalEventData.bytesTransferredOut - to: origin.bytesSent - - - rename: - from: - - log.additionalEventData.x-amz-id-2 - to: log.additionalEventDataXamzId2 - - - rename: - from: - - log.additionalEventData.SSEApplied - to: log.additionalEventDataSSEApplied - - - rename: - from: - - log.requestParameters.key - to: log.requestParametersKey - - - rename: - from: - - log.requestParameters.x-amz-acl - to: log.requestParametersXAmzAcl - - - rename: - from: - - log.requestParameters.x-amz-server-side-encryption - to: log.requestParametersXAmzServerSideEncryption - - - rename: - from: - - log.responseElements.x-amz-expiration - to: log.responseElementsXAmzExpiration - - - rename: - from: - - log.responseElements.x-amz-server-side-encryption - to: log.responseElementsXAmzServerSideEncryption - - - rename: - from: - - log.responseElements.assumedRoleUser - to: log.responseElementsAssumedRoleUser - - - rename: - from: - - log.responseElements.credentials - to: log.responseElementsCredentials - - - rename: - from: - - log.tlsDetails.cipherSuite - to: log.tlsDetailsCipherSuite - - - rename: - from: - - log.tlsDetails.clientProvidedHostHeader - to: log.tlsDetailsClientProvidedHostHeader - - - rename: - from: - - log.tlsDetails.tlsVersion - to: log.tlsDetailsTlsVersion - - # Adding action result - - add: - function: string - params: - key: actionResult - value: "success" - where: 'exists("log.eventName") && !exists("log.errorCode")' - - add: - function: string - params: - key: actionResult - value: "denied" - where: 'equals("log.errorCode", "AccessDenied")' - - add: - function: string - params: - key: actionResult - value: failure - where: exists("log.errorCode") && !equals("log.errorCode", "AccessDenied") - - - delete: - fields: - - log.requestParameters - - log.responseElements - - log.userIdentity - - log.additionalEventData - - # Promote standard fields while retaining vendor fields used by rules. - - grok: - source: log.sourceIPAddress - patterns: - - fieldName: origin.ip - pattern: '{{.greedy}}' - where: inCIDR("log.sourceIPAddress","0.0.0.0/0") || inCIDR("log.sourceIPAddress","::/0") - - grok: - source: log.userIdentityArn - patterns: - - fieldName: origin.user - pattern: '{{.greedy}}' - where: exists("log.userIdentityArn") && !exists("origin.user") - - grok: - source: log.eventTime - patterns: - - fieldName: deviceTime - pattern: '{{.greedy}}' - where: exists("log.eventTime") && !exists("deviceTime") +- dataTypes: + - aws + steps: + - json: + source: raw + - rename: + from: + - log.userIdentity.accessKeyId + to: log.userIdentityAccessKeyId + - rename: + from: + - log.userIdentity.accountId + to: log.userIdentityAccountId + - rename: + from: + - log.userIdentity.arn + to: log.userIdentityArn + - rename: + from: + - log.userIdentity.invokedBy + to: log.userIdentityInvokedBy + - rename: + from: + - log.userIdentity.principalId + to: log.userIdentityPrincipalId + - rename: + from: + - log.userIdentity.sessionContext.attributes.creationDate + to: log.userIdentitySessionContextAttributesCreationDate + - rename: + from: + - log.userIdentity.sessionContext.attributes.mfaAuthenticated + to: log.userIdentitySessionContextAttributesMfaAuthenticated + - rename: + from: + - log.userIdentity.sessionContext.sessionIssuer.accountId + to: log.userIdentitySessionContextSessionIssuerAccountId + - rename: + from: + - log.userIdentity.sessionContext.sessionIssuer.arn + to: log.userIdentitySessionContextSessionIssuerArn + - rename: + from: + - log.userIdentity.sessionContext.sessionIssuer.principalId + to: log.userIdentitySessionContextSessionIssuerPrincipalId + - rename: + from: + - log.userIdentity.sessionContext.sessionIssuer.type + to: log.userIdentitySessionContextSessionIssuerType + - rename: + from: + - log.userIdentity.sessionContext.sessionIssuer.userName + to: log.userIdentitySessionContextSessionIssuerUserName + - rename: + from: + - log.userIdentity.sessionContext.webIdFederationData + to: log.userIdentitySessionContextWebIdFederationData + - rename: + from: + - log.userIdentity.type + to: log.userIdentityType + - rename: + from: + - log.requestParameters.durationSeconds + to: log.requestParametersDurationSeconds + - rename: + from: + - log.requestParameters.roleArn + to: log.requestParametersRoleArn + - rename: + from: + - log.requestParameters.roleSessionName + to: log.requestParametersRoleSessionName + - rename: + from: + - log.responseElements.assumedRoleUser.arn + to: log.responseElementsAssumedRoleUserArn + - rename: + from: + - log.responseElements.assumedRoleUser.assumedRoleId + to: log.responseElementsAssumedRoleUserAssumedRoleId + - rename: + from: + - log.responseElements.credentials.accessKeyId + to: log.responseElementsCredentialsAccessKeyId + - rename: + from: + - log.responseElements.credentials.expiration + to: log.responseElementsCredentialsExpiration + - rename: + from: + - log.responseElements.credentials.sessionToken + to: log.responseElementsCredentialsSessionToken + - rename: + from: + - log.requestParameters.Host + to: log.requestParametersHost + - rename: + from: + - log.requestParameters.acl + to: log.requestParametersAcl + - rename: + from: + - log.requestParameters.bucketName + to: log.userIdentityAccessrequestParametersBucketNamesKeyId + - rename: + from: + - log.additionalEventData.SignatureVersion + to: log.additionalEventDataSignatureVersion + - rename: + from: + - log.additionalEventData.AuthenticationMethod + to: log.additionalEventDataAuthenticationMethod + - rename: + from: + - log.additionalEventData.CipherSuite + to: log.additionalEventDataCipherSuite + - rename: + from: + - log.additionalEventData.bytesTransferredIn + to: origin.bytesReceived + where: greaterOrEqual("log.additionalEventData.bytesTransferredIn",0) && lessOrEqual("log.additionalEventData.bytesTransferredIn",1.7976931348623157e308) + - rename: + from: + - log.additionalEventData.bytesTransferredOut + to: origin.bytesSent + where: greaterOrEqual("log.additionalEventData.bytesTransferredOut",0) && lessOrEqual("log.additionalEventData.bytesTransferredOut",1.7976931348623157e308) + - rename: + from: + - log.additionalEventData.xamzid2 + to: log.additionalEventDataXamzId2 + - rename: + from: + - log.additionalEventData.SSEApplied + to: log.additionalEventDataSSEApplied + - rename: + from: + - log.requestParameters.key + to: log.requestParametersKey + - rename: + from: + - log.requestParameters.xamzacl + to: log.requestParametersXAmzAcl + - rename: + from: + - log.requestParameters.xamzserversideencryption + to: log.requestParametersXAmzServerSideEncryption + - rename: + from: + - log.responseElements.xamzexpiration + to: log.responseElementsXAmzExpiration + - rename: + from: + - log.responseElements.xamzserversideencryption + to: log.responseElementsXAmzServerSideEncryption + - rename: + from: + - log.responseElements.assumedRoleUser + to: log.responseElementsAssumedRoleUser + - rename: + from: + - log.responseElements.credentials + to: log.responseElementsCredentials + - rename: + from: + - log.tlsDetails.cipherSuite + to: log.tlsDetailsCipherSuite + - rename: + from: + - log.tlsDetails.clientProvidedHostHeader + to: log.tlsDetailsClientProvidedHostHeader + - rename: + from: + - log.tlsDetails.tlsVersion + to: log.tlsDetailsTlsVersion + - json: + source: raw + - delete: + fields: + - log.awsRecordType + - log.awsActorKey + - log.awsActorKeyType + - log.awsAccountKey + - log.awsAccountKeyType + - log.correlationCandidate + - add: + function: string + params: + key: log.awsRecordType + value: cloudtrail + where: regexMatch("log.eventName","^[A-Za-z][A-Za-z0-9]*$") && regexMatch("log.eventVersion","^[0-9]+\\.[0-9]+$") && regexMatch("log.eventSource","^[A-Za-z0-9.-]+\\.amazonaws\\.com(\\.cn)?$") + - add: + function: string + params: + key: log.awsRecordType + value: guardduty + where: equals("log.source","aws.guardduty") && equals("log.detailtype","GuardDuty Finding") && exists("log.detail.id") && exists("log.detail.type") + - grok: + source: log.eventName + patterns: + - fieldName: action + pattern: (?s)^.+$ + where: regexMatch("log.eventName","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail")) + - grok: + source: log.sourceIPAddress + patterns: + - fieldName: origin.ip + pattern: (?s)^.+$ + where: regexMatch("log.sourceIPAddress","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && (inCIDR("log.sourceIPAddress","0.0.0.0/0") + || inCIDR("log.sourceIPAddress","::/0")) && !inCIDR("log.sourceIPAddress","0.0.0.0/32") && !inCIDR("log.sourceIPAddress","::/128")) + - grok: + source: log.sourceIPAddress + patterns: + - fieldName: origin.host + pattern: (?s)^.+$ + where: regexMatch("log.sourceIPAddress","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && !exists("origin.ip") && regexMatch("log.sourceIPAddress","^[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*\\.[A-Za-z][A-Za-z0-9-]*$")) + - grok: + source: log.userIdentity.arn + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: regexMatch("log.userIdentity.arn","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && regexMatch("log.userIdentity.arn","(?s)^.+$") + && !oneOf("log.userIdentity.arn",["","-","HIDDEN_DUE_TO_SECURITY_REASONS"])) + - grok: + source: log.userIdentity.userName + patterns: + - fieldName: origin.user + pattern: (?s)^.+$ + where: regexMatch("log.userIdentity.userName","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && !exists("origin.user") + && regexMatch("log.userIdentity.userName","(?s)^.+$") && !oneOf("log.userIdentity.userName",["","-","HIDDEN_DUE_TO_SECURITY_REASONS"])) + - grok: + source: log.eventSource + patterns: + - fieldName: target.domain + pattern: (?s)^.+$ + where: regexMatch("log.eventSource","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail")) + - grok: + source: log.eventTime + patterns: + - fieldName: deviceTime + pattern: (?s)^.+$ + where: regexMatch("log.eventTime","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && regexMatch("log.eventTime","^[0-9]{4}-(0[1-9]|1[0-2])-([0-2][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?(Z|[+-][0-2][0-9]:[0-5][0-9])$")) + - grok: + source: log.time + patterns: + - fieldName: deviceTime + pattern: (?s)^.+$ + where: regexMatch("log.time","(?s)^.+$") && (equals("log.awsRecordType","guardduty") && regexMatch("log.time","^[0-9]{4}-(0[1-9]|1[0-2])-([0-2][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?(Z|[+-][0-2][0-9]:[0-5][0-9])$")) + - grok: + source: log.requestParameters.bucketName + patterns: + - fieldName: log.requestParametersBucketName + pattern: (?s)^.+$ + where: regexMatch("log.requestParameters.bucketName","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail")) + - add: + function: string + params: + key: actionResult + value: failure + where: equals("log.awsRecordType","cloudtrail") && ((exists("log.errorCode") && !equals("log.errorCode","")) || (exists("log.errorMessage") + && !equals("log.errorMessage","")) || (exists("log.responseElements.errorCode") && !equals("log.responseElements.errorCode","")) + || (exists("log.responseElements.errorMessage") && !equals("log.responseElements.errorMessage","")) || equalsIgnoreCase("log.responseElements.ConsoleLogin","Failure") + || equalsIgnoreCase("log.responseElements.GetSigninToken","Failure") || equalsIgnoreCase("log.responseElements.CheckMfa","Failure")) + - add: + function: string + params: + key: actionResult + value: denied + where: equals("log.awsRecordType","cloudtrail") && (regexMatch("log.errorCode","(^|[.])(AccessDenied(?:Exception)?|VpceAccessDenied|UnauthorizedOperation|UnauthorizedException|NotAuthorizedException|AuthorizationError)$") + || regexMatch("log.responseElements.errorCode","(^|[.])(AccessDenied(?:Exception)?|VpceAccessDenied|UnauthorizedOperation|UnauthorizedException|NotAuthorizedException|AuthorizationError)$")) + - add: + function: string + params: + key: actionResult + value: success + where: equals("log.awsRecordType","cloudtrail") && !((exists("log.errorCode") && !equals("log.errorCode","")) || (exists("log.errorMessage") + && !equals("log.errorMessage","")) || (exists("log.responseElements.errorCode") && !equals("log.responseElements.errorCode","")) + || (exists("log.responseElements.errorMessage") && !equals("log.responseElements.errorMessage","")) || equalsIgnoreCase("log.responseElements.ConsoleLogin","Failure") + || equalsIgnoreCase("log.responseElements.GetSigninToken","Failure") || equalsIgnoreCase("log.responseElements.CheckMfa","Failure")) + && (equals("log.eventType","AwsApiCall") || (equals("log.eventName","ConsoleLogin") && equals("log.responseElements.ConsoleLogin","Success")) + || (equals("log.eventName","GetSigninToken") && equals("log.responseElements.GetSigninToken","Success")) || (equals("log.eventName","CheckMfa") + && equals("log.responseElements.CheckMfa","Success"))) + - grok: + source: log.recipientAccountId + patterns: + - fieldName: log.awsAccountKey + pattern: (?s)^.+$ + where: regexMatch("log.recipientAccountId","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && regexMatch("log.recipientAccountId","^[0-9]{12}$") + && !exists("log.awsAccountKey")) + - add: + function: string + params: + key: log.awsAccountKeyType + value: recipient + where: exists("log.awsAccountKey") && !exists("log.awsAccountKeyType") + - grok: + source: log.userIdentity.accountId + patterns: + - fieldName: log.awsAccountKey + pattern: (?s)^.+$ + where: regexMatch("log.userIdentity.accountId","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && regexMatch("log.userIdentity.accountId","^[0-9]{12}$") + && !exists("log.awsAccountKey")) + - add: + function: string + params: + key: log.awsAccountKeyType + value: caller + where: exists("log.awsAccountKey") && !exists("log.awsAccountKeyType") + - grok: + source: log.userIdentity.arn + patterns: + - fieldName: log.awsActorKey + pattern: (?s)^.+$ + where: regexMatch("log.userIdentity.arn","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && regexMatch("log.userIdentity.arn","(?s)^.+$") + && !oneOf("log.userIdentity.arn",["","-","HIDDEN_DUE_TO_SECURITY_REASONS"]) && !exists("log.awsActorKey")) + - add: + function: string + params: + key: log.awsActorKeyType + value: arn + where: exists("log.awsActorKey") && !exists("log.awsActorKeyType") + - grok: + source: log.userIdentity.principalId + patterns: + - fieldName: log.awsActorKey + pattern: (?s)^.+$ + where: regexMatch("log.userIdentity.principalId","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && regexMatch("log.userIdentity.principalId","(?s)^.+$") + && !oneOf("log.userIdentity.principalId",["","-","HIDDEN_DUE_TO_SECURITY_REASONS"]) && !exists("log.awsActorKey")) + - add: + function: string + params: + key: log.awsActorKeyType + value: principal + where: exists("log.awsActorKey") && !exists("log.awsActorKeyType") + - grok: + source: origin.ip + patterns: + - fieldName: log.awsActorKey + pattern: (?s)^.+$ + where: regexMatch("origin.ip","(?s)^.+$") && (equals("log.awsRecordType","cloudtrail") && regexMatch("origin.ip","(?s)^.+$") && + !oneOf("origin.ip",["","-","HIDDEN_DUE_TO_SECURITY_REASONS"]) && !exists("log.awsActorKey")) + - add: + function: string + params: + key: log.awsActorKeyType + value: ip + where: exists("log.awsActorKey") && !exists("log.awsActorKeyType") + - add: + function: string + params: + key: severity + value: info + where: equals("log.awsRecordType","guardduty") && greaterOrEqual("log.detail.severity",1) && lessOrEqual("log.detail.severity",3.9) + - add: + function: string + params: + key: severity + value: warning + where: equals("log.awsRecordType","guardduty") && greaterOrEqual("log.detail.severity",4) && lessOrEqual("log.detail.severity",6.9) + - add: + function: string + params: + key: severity + value: error + where: equals("log.awsRecordType","guardduty") && greaterOrEqual("log.detail.severity",7) && lessOrEqual("log.detail.severity",8.9) + - add: + function: string + params: + key: severity + value: critical + where: equals("log.awsRecordType","guardduty") && greaterOrEqual("log.detail.severity",9) && lessOrEqual("log.detail.severity",10) + - dynamic: + plugin: com.utmstack.geolocation + params: + source: origin.ip + destination: origin.geolocation + where: equals("log.awsRecordType","cloudtrail") && exists("origin.ip") + - add: + function: string + params: + key: log.correlationCandidate.aws_ecs_credential_theft + value: match + where: |- + equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "ecs.amazonaws.com") && + oneOf("log.eventName", ["DescribeTaskDefinition", "RunTask", "StartTask"]) && + equals("actionResult","success") && + equals("log.userIdentity.type", "AssumedRole") && + contains("log.userIdentity.arn", ":assumed-role/")) + - add: + function: string + params: + key: log.correlationCandidate.saml_provider_change + value: match + where: equals("log.awsRecordType","cloudtrail") && equals("log.eventSource","iam.amazonaws.com") && oneOf("log.eventName",["CreateSAMLProvider", + "UpdateSAMLProvider"]) && equals("actionResult","success") + - add: + function: string + params: + key: log.correlationCandidate.aws_securityhub_finding_evasion + value: match + where: |- + equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "securityhub.amazonaws.com") && + oneOf("log.eventName", ["BatchUpdateFindings", "DeleteInsight", "UpdateFindings", "DeleteActionTarget"]) && + equals("actionResult","success") && + !equals("log.userIdentity.type", "AWSService")) + - add: + function: string + params: + key: log.correlationCandidate.aws_ssm_sendcommand_abuse + value: match + where: |- + equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "ssm.amazonaws.com") && + oneOf("log.eventName", ["SendCommand", "StartSession"]) && + equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.aws_sso_suspicious_activities + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","sso.amazonaws.com") && oneOf("log.eventName",["CreatePermissionSet", + "AttachManagedPolicyToPermissionSet", "DeletePermissionSet", "PutInlinePolicyToPermissionSet"]) && equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.cloudformation_stack_deletion + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "cloudformation.amazonaws.com") && equals("log.eventName", + "DeleteStack") && equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.console_login_impossible_travel + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","signin.amazonaws.com") && oneOf("log.eventName",["ConsoleLogin"]) + && equals("actionResult","success") && regexMatch("origin.geolocation.countryCode","^[A-Z]{2}$")) + - add: + function: string + params: + key: log.correlationCandidate.cross_account_access_anomalies + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","sts.amazonaws.com") && oneOf("log.eventName",["AssumeRole"]) + && equals("actionResult","success") && regexMatch("log.userIdentity.accountId","^[0-9]{12}$") && regexMatch("log.responseElements.assumedRoleUser.arn","^arn:[^:]+:sts::[0-9]{12}:assumed-role/.+$") + && !regexMatch("log.responseElements.assumedRoleUser.arn","^arn:[^:]+:sts::" + safe("log.userIdentity.accountId", "") + ":assumed-role/")) + - add: + function: string + params: + key: log.correlationCandidate.iam_backdoor_creation_attempts + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "iam.amazonaws.com") && oneOf("log.eventName", ["CreateUser", + "CreateAccessKey", "AttachUserPolicy", "PutUserPolicy", "CreateLoginProfile"]) && equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.iam_privilege_escalation_paths + value: match + where: |- + equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "iam.amazonaws.com") && + oneOf("log.eventName", ["CreatePolicyVersion", "SetDefaultPolicyVersion", "AttachUserPolicy", "AttachGroupPolicy", "AttachRolePolicy", "PutUserPolicy", "PutGroupPolicy", "PutRolePolicy", "AddUserToGroup"]) && + equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.lambda_privilege_escalation + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","iam.amazonaws.com") && oneOf("log.eventName",["AttachRolePolicy", + "AttachUserPolicy"]) && equals("actionResult","success") && regexMatch("log.requestParameters.policyArn","^arn:(aws|aws-cn|aws-us-gov):iam::aws:policy/(AdministratorAccess|IAMFullAccess)$")) + - add: + function: string + params: + key: log.correlationCandidate.mass_resource_deletion + value: match + where: equals("log.awsRecordType","cloudtrail") && (regexMatch("log.eventName","^(Delete|Terminate|Remove)[A-Z]") && equals("actionResult","success") + && !equals("log.eventSource","s3.amazonaws.com")) + - add: + function: string + params: + key: log.correlationCandidate.route53_dns_hijacking + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "route53.amazonaws.com") && equals("log.eventName", + "ChangeResourceRecordSets") && equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.s3_bulk_data_exfiltration + value: match + where: |- + equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "s3.amazonaws.com") && + equals("log.eventName", "GetObject") && + equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.secrets_manager_suspicious_access + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "secretsmanager.amazonaws.com") && (equals("log.eventName", + "GetSecretValue") || equals("log.eventName", "BatchGetSecretValue")) && equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.security_group_modifications + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","ec2.amazonaws.com") && oneOf("log.eventName",["AuthorizeSecurityGroupIngress", + "AuthorizeSecurityGroupEgress", "ModifySecurityGroupRules"]) && equals("actionResult","success") && (exists('log.requestParameters.ipPermissions.items.#(ipRanges.items.#(cidrIp=="0.0.0.0/0"))') + || exists('log.requestParameters.ipPermissions.items.#(ipv6Ranges.items.#(cidrIpv6=="::/0"))') || exists('log.requestParameters.securityGroupRuleSet.items.#(securityGroupRule.cidrIpv4=="0.0.0.0/0")') + || exists('log.requestParameters.securityGroupRuleSet.items.#(securityGroupRule.cidrIpv6=="::/0")'))) + - add: + function: string + params: + key: log.correlationCandidate.ssm_session_abuse + value: match + where: |- + equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "ssm.amazonaws.com") && + oneOf("log.eventName", ["StartSession", "ResumeSession", "SendCommand", "StartAutomationExecution"]) && + equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.sts_token_abuse + value: match + where: |- + equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource", "sts.amazonaws.com") && + equals("log.eventName", "AssumeRole") && + equals("actionResult","success") && + equals("log.userIdentity.sessionContext.attributes.mfaAuthenticated", "false") && + !equals("log.userIdentity.type", "AWSService") && + !contains("log.userAgent", "aws-sdk") && + !contains("log.userAgent", "Botocore")) + - add: + function: string + params: + key: log.correlationCandidate.unusual_api_call_patterns + value: match + where: "equals(\"log.awsRecordType\",\"cloudtrail\") && (exists(\"log.eventSource\") && \nexists(\"log.sourceIPAddress\") &&\n\ + exists(\"log.eventName\") &&\n(\n oneOf(\"log.eventName\", [\"DescribeSecurityGroups\", \"DescribeNetworkAcls\", \"DescribeVpcs\"\ + , \"DescribeSubnets\", \"DescribeRouteTables\", \"DescribeInstances\", \"DescribeSnapshots\", \"DescribeVolumes\", \"DescribeImages\"\ + , \"DescribeKeyPairs\", \"ListBuckets\", \"GetBucketAcl\", \"GetBucketPolicy\", \"ListAccessKeys\", \"ListUsers\", \"ListRoles\"\ + , \"ListPolicies\", \"GetAccountAuthorizationDetails\", \"GenerateCredentialReport\", \"GetCredentialReport\"])\n) &&\nequals(\"\ + actionResult\",\"success\"))" + - add: + function: string + params: + key: log.correlationCandidate.vpc_flow_log_anomalies + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","ec2.amazonaws.com") && oneOf("log.eventName",["DeleteFlowLogs"]) + && equals("actionResult","success")) + - add: + function: string + params: + key: log.correlationCandidate.credential_access_aws_iam_assume_role_brute_force + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","iam.amazonaws.com") && oneOf("log.eventName",["UpdateAssumeRolePolicy"]) + && oneOf("log.errorCode",["MalformedPolicyDocument", "MalformedPolicyDocumentException"]) && equals("actionResult","failure")) + - add: + function: string + params: + key: log.correlationCandidate.credential_access_root_console_failure_brute_force + value: match + where: equals("log.awsRecordType","cloudtrail") && (equals("log.eventSource","signin.amazonaws.com") && oneOf("log.eventName",["ConsoleLogin"]) + && equals("log.userIdentity.type","Root") && oneOf("actionResult",["failure", "denied"])) diff --git a/plugins/alerts/aws_contract_test.go b/plugins/alerts/aws_contract_test.go new file mode 100644 index 000000000..60c9de5b3 --- /dev/null +++ b/plugins/alerts/aws_contract_test.go @@ -0,0 +1,490 @@ +package main + +// Offline AWS extraction model, not the closed EventProcessor. +// Explicit YAML JSON/key sanitization, grok, rename, add and delete steps are modeled. +// CEL and Event serialization use SDK v1.1.31. History requests are tested separately +// with that SDK. External geolocation is mocked only when a fixture declares it. +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + "reflect" + "regexp" + "strings" + "testing" + "text/template" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/threatwinds/go-sdk/utils" + "github.com/tidwall/gjson" + "google.golang.org/protobuf/encoding/protojson" +) + +type awsFixture struct { + Name string `json:"name"` + DataSource string `json:"dataSource"` + Raw string `json:"raw"` + Expected map[string]any `json:"expected"` + Absent []string `json:"absent"` + Matches []string `json:"matches"` + Enrichment map[string]any `json:"enrichment"` +} + +func awsPut(m map[string]any, path string, value any, remove bool) { + p := strings.Split(path, ".") + for _, k := range p[:len(p)-1] { + n, ok := m[k].(map[string]any) + if !ok { + if remove { + return + } + n = map[string]any{} + m[k] = n + } + m = n + } + if remove { + delete(m, p[len(p)-1]) + } else { + m[p[len(p)-1]] = value + } +} +func awsGet(m map[string]any, p string) (any, bool) { + var v any = m + for _, k := range strings.Split(p, ".") { + n, ok := v.(map[string]any) + if !ok { + return nil, false + } + v, ok = n[k] + if !ok { + return nil, false + } + } + return v, true +} +func awsConfig(t *testing.T) *plugins.Config { + t.Helper() + b, e := utils.ReadPbYaml("../../filters/aws/aws.yml") + if e != nil { + t.Fatal(e) + } + c := new(plugins.Config) + if e = protojson.Unmarshal(b, c); e != nil { + t.Fatal(e) + } + return c +} +func awsRegex(t *testing.T, g *plugins.Grok, cfg *plugins.Config) *regexp.Regexp { + t.Helper() + var pattern strings.Builder + for i, p := range g.Patterns { + if p.FieldName != "" { + fmt.Fprintf(&pattern, "(?P%s)", i, p.Pattern) + } else { + pattern.WriteString("(?:" + p.Pattern + ")") + } + } + pats := map[string]string{"greedy": ".*", "data": ".*?", "word": "[A-Za-z0-9_-]+", "space": "\\s+"} + for k, v := range cfg.Patterns { + pats[k] = v + } + tmpl, e := template.New("grok").Option("missingkey=error").Parse(pattern.String()) + if e != nil { + t.Fatal(e) + } + var b bytes.Buffer + if e = tmpl.Execute(&b, pats); e != nil { + t.Fatal(e) + } + r, e := regexp.Compile(b.String()) + if e != nil { + t.Fatal(e) + } + return r +} +func awsParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache, enrichment ...map[string]any) string { + t.Helper() + draft := map[string]any{"raw": raw, "dataType": "aws", "dataSource": dataSource, "log": map[string]any{}} + for _, stage := range cfg.Pipeline { + matched := false + for _, dataType := range stage.DataTypes { + if dataType == "aws" { + matched = true + } + } + if !matched { + continue + } + for _, s := range stage.Steps { + b, e := protojson.Marshal(s) + if e != nil { + t.Fatal(e) + } + var step map[string]map[string]any + if e = json.Unmarshal(b, &step); e != nil { + t.Fatal(e) + } + for kind, body := range step { + if w, ok := body["where"].(string); ok && w != "" { + snapshot, err := json.Marshal(draft) + if err != nil { + t.Fatal(err) + } + match, e := cache.Eval(w, string(snapshot)) + if e != nil { + t.Fatal(e) + } + if !match { + continue + } + } + switch kind { + case "grok": + g := s.Grok + src := g.Source + if src == "" { + src = "raw" + } + v, ok := awsGet(draft, src) + if !ok { + continue + } + str, ok := v.(string) + if !ok { + t.Fatalf("non-string grok source %s", src) + } + r := awsRegex(t, g, cfg) + m := r.FindStringSubmatch(str) + if m == nil { + continue + } + for i, p := range g.Patterns { + if p.FieldName != "" { + awsPut(draft, p.FieldName, m[r.SubexpIndex(fmt.Sprintf("f%d", i))], false) + } + } + case "rename": + for _, p := range s.Rename.From { + if v, ok := awsGet(draft, p); ok { + awsPut(draft, s.Rename.To, v, false) + awsPut(draft, p, nil, true) + break + } + } + case "add": + if s.Add.Function != "string" { + t.Fatalf("unsupported add function %s", s.Add.Function) + } + awsPut(draft, s.Add.Params["key"].GetStringValue(), s.Add.Params["value"].AsInterface(), false) + case "delete": + for _, p := range s.Delete.Fields { + awsPut(draft, p, nil, true) + } + case "dynamic": + if s.Dynamic.Plugin != "com.utmstack.geolocation" { + t.Fatalf("unsupported dynamic plugin %s", s.Dynamic.Plugin) + } + field := s.Dynamic.Params["source"].GetStringValue() + v, ok := awsGet(draft, field) + if !ok { + t.Fatalf("missing dynamic source %s", field) + } + ip := net.ParseIP(fmt.Sprint(v)) + if ip == nil || ip.IsUnspecified() { + t.Fatalf("invalid address reaches geolocation: %s", field) + } + // The external geolocation service is not executed. Fixtures may explicitly supply its mocked output. + for _, fields := range enrichment { + for path, value := range fields { + if !strings.HasPrefix(path, "origin.geolocation.") { + t.Fatal("unexpected enrichment field") + } + awsPut(draft, path, value, false) + } + } + case "json": + source, ok := awsGet(draft, s.Json.Source) + if !ok { + continue + } + str, ok := source.(string) + if !ok { + t.Fatalf("JSON source is not a string") + } + var parsed map[string]any + if e := json.Unmarshal([]byte(str), &parsed); e != nil { + t.Fatal(e) + } + for key, value := range awsSanitizeJSON(parsed) { + awsPut(draft, "log."+key, value, false) + } + case "cast": + for _, field := range s.Cast.Fields { + if value, ok := awsGet(draft, field); ok { + switch s.Cast.To { + case "string": + awsPut(draft, field, utils.CastString(value), false) + case "int": + awsPut(draft, field, utils.CastInt64(value), false) + default: + t.Fatalf("unsupported cast %s", s.Cast.To) + } + } + } + case "drop": + return "" + default: + t.Fatalf("unsupported filter step %s", kind) + } + } + } + } + b, e := json.Marshal(draft) + if e != nil { + t.Fatal(e) + } + in := string(b) + ev := new(plugins.Event) + if e = utils.StringToProtoMessage(&in, ev); e != nil { + t.Fatal(e) + } + out, e := utils.ProtoMessageToString(ev) + if e != nil { + t.Fatal(e) + } + return *out +} +func awsRules(t *testing.T) map[string]*plugins.Rule { + t.Helper() + paths := []string{} + e := filepath.WalkDir("../../rules/cloud/aws", func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() && (strings.HasSuffix(path, ".yml") || strings.HasSuffix(path, ".yaml")) { + paths = append(paths, path) + } + return nil + }) + if e != nil { + t.Fatal(e) + } + + out := map[string]*plugins.Rule{} + for _, p := range paths { + b, e := utils.ReadPbYaml(p) + if e != nil { + t.Fatal(e) + } + r := new(plugins.Rule) + if e = protojson.Unmarshal(b, r); e != nil { + t.Fatal(e) + } + r.Normalize() + out[strings.TrimSuffix(filepath.Base(p), filepath.Ext(p))] = r + } + return out +} + +func awsSanitizeJSON(input map[string]any) map[string]any { + var walk func(any) any + walk = func(input any) any { + switch v := input.(type) { + case map[string]any: + out := map[string]any{} + for key, value := range v { + utils.SanitizeField(&key) + out[key] = walk(value) + } + return out + case []any: + out := make([]any, len(v)) + for i, x := range v { + out[i] = walk(x) + } + return out + default: + return input + } + } + return walk(input).(map[string]any) +} + +func awsFixtures(t *testing.T) []awsFixture { + t.Helper() + b, e := os.ReadFile("testdata/aws_raw.json") + if e != nil { + t.Fatal(e) + } + var cases []awsFixture + if e = json.Unmarshal(b, &cases); e != nil { + t.Fatal(e) + } + return cases +} + +func TestAWSRawContracts(t *testing.T) { + cfg, rules, cache := awsConfig(t), awsRules(t), plugins.NewCELCache("aws-raw") + positive := map[string]int{} + negative := map[string]int{} + if len(rules) != 73 { + t.Fatalf("rules: %d", len(rules)) + } + for _, f := range awsFixtures(t) { + t.Run(f.Name, func(t *testing.T) { + out := awsParse(t, cfg, f.Raw, f.DataSource, cache, f.Enrichment) + for field, want := range f.Expected { + got := gjson.Get(out, field) + if !got.Exists() || !reflect.DeepEqual(got.Value(), want) { + t.Errorf("%s got %v want %v", field, got.Value(), want) + } + } + for _, field := range f.Absent { + if gjson.Get(out, field).Exists() { + t.Errorf("unexpected %s", field) + } + } + if gjson.Get(out, "raw").String() != f.Raw { + t.Error("raw altered") + } + expected := map[string]bool{} + for _, n := range f.Matches { + expected[n] = true + } + for name, r := range rules { + yes, e := cache.Eval(r.Where, out) + if e != nil { + t.Fatalf("%s: %v", name, e) + } + if yes != expected[name] { + t.Errorf("%s matched %v want %v", name, yes, expected[name]) + } + if yes { + positive[name]++ + } else { + negative[name]++ + } + if yes { + for _, search := range r.Correlation { + for _, term := range search.With { + value := term.Value.GetStringValue() + if strings.HasPrefix(value, "{{.") { + field := strings.TrimSuffix(strings.TrimPrefix(value, "{{."), "}}") + if !gjson.Get(out, field).Exists() { + t.Errorf("%s unresolved %s", name, field) + } + } + } + } + } + if yes { + ev := new(plugins.Event) + if e := utils.StringToProtoMessage(&out, ev); e != nil { + t.Fatal(e) + } + if r.Adversary != "origin" { + t.Errorf("unexpected actor direction %s", r.Adversary) + } + alert := &plugins.Alert{Adversary: ev.Origin, Target: ev.Target, Events: []*plugins.Event{ev}} + wire, e := utils.ProtoMessageToString(alert) + if e != nil { + t.Fatal(e) + } + if gjson.Get(out, "target.ip").String() != gjson.Get(*wire, "target.ip").String() { + t.Error("endpoint identity lost") + } + if gjson.Get(out, "origin.ip").String() != gjson.Get(*wire, "adversary.ip").String() { + t.Error("attacker identity lost") + } + } + } + }) + } + for name := range rules { + if positive[name] == 0 || negative[name] == 0 { + t.Errorf("%s missing positive/negative coverage: %d/%d", name, positive[name], negative[name]) + } + } +} +func TestAWSOfficialExamples(t *testing.T) { + input := os.Getenv("AWS_OFFICIAL_EXAMPLES") + if input == "" { + t.Skip("official documentation snapshots supplied separately") + } + b, e := os.ReadFile(input) + if e != nil { + t.Fatal(e) + } + var docs []struct { + Reference string `json:"reference"` + Event map[string]any `json:"event"` + Expected map[string]any `json:"expected"` + Absent []string `json:"absent"` + Matches []string `json:"matches"` + } + if e = json.Unmarshal(b, &docs); e != nil { + t.Fatal(e) + } + cfg, rules, cache := awsConfig(t), awsRules(t), plugins.NewCELCache("aws-official") + results := []map[string]any{} + for _, d := range docs { + raw, e := json.Marshal(d.Event) + if e != nil { + t.Fatal(e) + } + parsed := awsParse(t, cfg, string(raw), "collector-test", cache) + if len(d.Expected) == 0 || d.Matches == nil { + t.Fatal("official sample lacks explicit expectations") + } + for path, want := range d.Expected { + if got := gjson.Get(parsed, path); !got.Exists() || !reflect.DeepEqual(got.Value(), want) { + t.Errorf("%s %s: %s got %v want %v", d.Reference, d.Event["eventName"], path, got.Value(), want) + } + } + for _, path := range d.Absent { + if gjson.Get(parsed, path).Exists() { + t.Errorf("unexpected %s", path) + } + } + expectedMatches := map[string]bool{} + for _, name := range d.Matches { + expectedMatches[name] = true + } + matches := []string{} + errors := map[string]string{} + for name, r := range rules { + yes, err := cache.Eval(r.Where, parsed) + if err != nil { + errors[name] = err.Error() + t.Errorf("official predicate %s: %v", name, err) + } else if yes { + matches = append(matches, name) + } + } + for name, r := range rules { + yes, e := cache.Eval(r.Where, parsed) + if e != nil || yes != expectedMatches[name] { + t.Errorf("official %s %s matched %v want %v: %v", d.Event["eventName"], name, yes, expectedMatches[name], e) + } + } + var value map[string]any + if e = json.Unmarshal([]byte(parsed), &value); e != nil { + t.Fatal(e) + } + results = append(results, map[string]any{"reference": d.Reference, "parsed": value, "matches": matches, "errors": errors}) + } + if path := os.Getenv("AWS_OFFICIAL_OUTPUT"); path != "" { + b, e := json.MarshalIndent(results, "", " ") + if e != nil { + t.Fatal(e) + } + if e = os.WriteFile(path, b, 0600); e != nil { + t.Fatal(e) + } + } + t.Logf("modeled %d official examples; no live AWS telemetry or alerts", len(docs)) +} diff --git a/plugins/alerts/aws_history_test.go b/plugins/alerts/aws_history_test.go new file mode 100644 index 000000000..e48aec0aa --- /dev/null +++ b/plugins/alerts/aws_history_test.go @@ -0,0 +1,328 @@ +package main + +// Offline history requests use the real SDK and an isolated loopback mock. +// The mock evaluates only the term/not-term/time clauses asserted below. +import ( + "encoding/json" + "fmt" + sdkos "github.com/threatwinds/go-sdk/os" + "github.com/threatwinds/go-sdk/plugins" + "github.com/tidwall/gjson" + "io" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "strings" + "testing" + "time" +) + +func TestAWSSDKHistory(t *testing.T) { + if os.Getenv("UTM_AWS_HISTORY_CHILD") != "1" { + c := exec.Command(os.Args[0], "-test.run=^TestAWSSDKHistory$") + c.Env = append(os.Environ(), "UTM_AWS_HISTORY_CHILD=1") + if b, e := c.CombinedOutput(); e != nil { + t.Fatalf("isolated history: %v\n%s", e, b) + } + return + } + cfg, rules, cache := awsConfig(t), awsRules(t), plugins.NewCELCache("aws-history") + var history []string + var terms, notTerms map[string]string + var window time.Duration + var ruleName string + queries := 0 + mapping := map[string]any{"properties": map[string]any{}} + props := mapping["properties"].(map[string]any) + paths := []string{"dataSource", "log.awsAccountKeyType", "log.awsAccountKey", "log.awsActorKeyType", "log.awsActorKey", "origin.ip", "origin.geolocation.countryCode", "log.eventName", "log.correlationCandidate.saml_provider_change"} + for name, r := range rules { + if len(r.Correlation) > 0 { + paths = append(paths, "log.correlationCandidate."+name) + } + } + for _, path := range paths { + node := props + parts := strings.Split(path, ".") + for _, part := range parts[:len(parts)-1] { + if node[part] == nil { + node[part] = map[string]any{"properties": map[string]any{}} + } + node = node[part].(map[string]any)["properties"].(map[string]any) + } + node[parts[len(parts)-1]] = map[string]any{"type": "text", "fields": map[string]any{"keyword": map[string]any{"type": "keyword"}}} + } + props["@timestamp"] = map[string]any{"type": "date"} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + if strings.HasSuffix(r.URL.Path, "/_mapping") { + _ = json.NewEncoder(w).Encode(map[string]any{"v11-log-aws-test": map[string]any{"mappings": mapping}}) + return + } + if r.URL.Path != "/v11-log-aws-*/_search" { + t.Errorf("unexpected request %s", r.URL.Path) + http.Error(w, "bad request", 400) + return + } + queries++ + body, e := io.ReadAll(r.Body) + if e != nil { + t.Error(e) + return + } + q := string(body) + clauses := append(gjson.Get(q, "query.bool.filter").Array(), gjson.Get(q, "query.bool.must").Array()...) + negatives := gjson.Get(q, "query.bool.must_not").Array() + gotTerms := map[string]string{} + gotNot := map[string]string{} + cutoff := time.Time{} + for _, clause := range clauses { + if term := clause.Get("term"); term.Exists() { + for field, value := range term.Map() { + gotTerms[strings.TrimSuffix(field, ".keyword")] = value.Get("value").String() + } + } else if span := clause.Get("range"); span.Exists() { + cutoff, e = time.Parse(time.RFC3339Nano, span.Get("@timestamp.gte").String()) + if e != nil { + t.Error(e) + } + } else { + t.Errorf("unsupported clause %s", clause.Raw) + } + } + for _, clause := range negatives { + if nested := clause.Get("bool.must"); nested.Exists() { + if len(nested.Array()) != 1 { + t.Error("unexpected negative bool") + } + clause = nested.Array()[0] + } + if term := clause.Get("term"); term.Exists() { + for field, value := range term.Map() { + gotNot[strings.TrimSuffix(field, ".keyword")] = value.Get("value").String() + } + } else { + t.Errorf("unsupported negative %s", clause.Raw) + } + } + same := func(a, b map[string]string) bool { + if len(a) != len(b) { + return false + } + for k, v := range a { + if b[k] != v { + return false + } + } + return true + } + expectedTerms := map[string]string{} + for k, v := range terms { + expectedTerms[k] = v + } + if ruleName == "secrets_manager_suspicious_access" { + value := gotTerms["log.eventName"] + if value != "GetSecretValue" && value != "BatchGetSecretValue" { + t.Error("unexpected secret query") + } + expectedTerms["log.eventName"] = value + } + if !same(gotTerms, expectedTerms) || !same(gotNot, notTerms) { + t.Errorf("scope mismatch: terms=%v negatives=%v", gotTerms, gotNot) + } + if delta := time.Since(cutoff) - window; delta < -2*time.Second || delta > 2*time.Second { + t.Errorf("unexpected time cutoff %v", delta) + } + hits := []map[string]any{} + for _, doc := range history { + match := true + for f, v := range gotTerms { + if !gjson.Get(doc, f).Exists() || gjson.Get(doc, f).String() != v { + match = false + } + } + for f, v := range gotNot { + if gjson.Get(doc, f).String() == v { + match = false + } + } + stamp, e := time.Parse(time.RFC3339Nano, gjson.Get(doc, "@timestamp").String()) + if e != nil || stamp.Before(cutoff) { + match = false + } + if match { + hits = append(hits, map[string]any{"_id": fmt.Sprint(len(hits)), "_index": "v11-log-aws-test", "_source": map[string]any{}}) + } + } + _ = json.NewEncoder(w).Encode(map[string]any{"took": 1, "hits": map[string]any{"total": map[string]any{"value": len(hits), "relation": "eq"}, "hits": hits}}) + })) + defer server.Close() + if e := sdkos.Connect([]string{server.URL}, "", ""); e != nil { + t.Fatal(e) + } + mutate := func(doc, path string, value any) string { + var m map[string]any + if e := json.Unmarshal([]byte(doc), &m); e != nil { + t.Fatal(e) + } + awsPut(m, path, value, value == nil) + b, e := json.Marshal(m) + if e != nil { + t.Fatal(e) + } + return string(b) + } + + cases := []struct { + rule, fixture, within, mode string + count uint64 + }{ + {"aws_ecs_credential_theft", "ecs-assumed-role", "30m", "ip", 5}, + {"aws_golden_saml_attack", "sts-AssumeRoleWithSAML", "24h", "account", 1}, + {"aws_securityhub_finding_evasion", "securityhub-BatchUpdateFindings", "30m", "actor", 5}, + {"aws_ssm_sendcommand_abuse", "ssm-SendCommand", "30m", "actor", 5}, + {"aws_sso_suspicious_activities", "sso-CreatePermissionSet", "30m", "ip", 10}, + {"cloudformation_stack_deletion", "cloudformation-DeleteStack", "30m", "actor", 5}, + {"console_login_impossible_travel", "console-country-correlation", "30m", "actor", 1}, + {"cross_account_access_anomalies", "cross-account-role", "15m", "ip", 15}, + {"iam_backdoor_creation_attempts", "iam-CreateUser", "30m", "ip", 3}, + {"iam_privilege_escalation_paths", "iam-AddUserToGroup", "30m", "actor", 3}, + {"lambda_privilege_escalation", "iam-AttachRolePolicy", "1h", "actor", 2}, + {"mass_resource_deletion", "backup-DeleteBackupVault", "10m", "actor", 15}, + {"route53_dns_hijacking", "route53-ChangeResourceRecordSets", "30m", "actor", 10}, + {"s3_bulk_data_exfiltration", "s3-GetObject", "15m", "actor", 100}, + {"secrets_manager_suspicious_access", "secretsmanager-GetSecretValue", "10m", "actor", 10}, + {"security_group_modifications", "ec2-AuthorizeSecurityGroupIngress", "30m", "ip", 3}, + {"ssm_session_abuse", "ssm-SendCommand", "30m", "actor", 5}, + {"sts_token_abuse", "sts-explicit-no-mfa", "15m", "ip", 20}, + {"unusual_api_call_patterns", "ec2-DescribeNetworkAcls", "10m", "ip", 50}, + {"vpc_flow_log_anomalies", "ec2-DeleteFlowLogs", "24h", "ip", 2}, + {"credential_access_aws_iam_assume_role_brute_force", "malformed-trust-policy", "15m", "actor", 5}, + {"credential_access_root_console_failure_brute_force", "root-Failure-Yes", "15m", "ip", 5}, + } + fixtures := map[string]awsFixture{} + for _, f := range awsFixtures(t) { + fixtures[f.Name] = f + } + for _, tc := range cases { + t.Run(tc.rule, func(t *testing.T) { + ruleName = tc.rule + r := rules[tc.rule] + if r == nil || len(r.Correlation) != 1 { + t.Fatal("missing or extra history") + } + search := r.Correlation[0] + if search.Count != tc.count || search.Within != tc.within { + t.Fatal("threshold/window changed") + } + var e error + window, e = time.ParseDuration(tc.within) + if e != nil { + t.Fatal(e) + } + f := fixtures[tc.fixture] + out := awsParse(t, cfg, f.Raw, f.DataSource, cache, f.Enrichment) + if yes, e := cache.Eval(r.Where, out); e != nil || !yes { + t.Fatalf("raw trigger failed: %v %v", yes, e) + } + marker := "log.correlationCandidate." + tc.rule + if tc.rule == "aws_golden_saml_attack" { + marker = "log.correlationCandidate.saml_provider_change" + } + terms = map[string]string{"dataSource": "collector-test", "log.awsAccountKeyType": "recipient", "log.awsAccountKey": "123456789012", marker: "match"} + notTerms = map[string]string{} + if tc.mode == "ip" { + terms["origin.ip"] = "198.51.100.10" + } + if tc.mode == "actor" { + terms["log.awsActorKeyType"] = "arn" + terms["log.awsActorKey"] = "arn:aws:iam::123456789012:user/reviewer" + } + prior := out + if tc.rule == "aws_golden_saml_attack" { + var raw map[string]any + if e := json.Unmarshal([]byte(f.Raw), &raw); e != nil { + t.Fatal(e) + } + raw["eventSource"] = "iam.amazonaws.com" + raw["eventName"] = "UpdateSAMLProvider" + raw["userIdentity"] = map[string]any{"type": "IAMUser", "accountId": "123456789012", "arn": "arn:aws:iam::123456789012:user/other-admin"} + encoded, _ := json.Marshal(raw) + prior = awsParse(t, cfg, string(encoded), f.DataSource, cache) + if !gjson.Get(prior, marker).Exists() { + t.Fatal("provider change does not produce sequence candidate") + } + if gjson.Get(out, marker).Exists() { + t.Fatal("SAML login counts as its own provider change") + } + } + if tc.rule == "console_login_impossible_travel" { + notTerms["origin.geolocation.countryCode"] = "US" + prior = mutate(prior, "origin.geolocation.countryCode", "GB") + } + prior = mutate(prior, "@timestamp", time.Now().Add(-time.Minute).UTC().Format(time.RFC3339Nano)) + check := func(name, doc string, count uint64, want bool) { + t.Run(name, func(t *testing.T) { + history = nil + for i := uint64(0); i < count; i++ { + history = append(history, doc) + } + yes, _, e := search.Execute(&out) + if e != nil || yes != want { + t.Fatalf("history %v want %v: %v", yes, want, e) + } + }) + } + check("below_threshold", prior, tc.count-1, false) + check("at_threshold", prior, tc.count, true) + check("expired", mutate(prior, "@timestamp", time.Now().Add(-window-time.Minute).UTC().Format(time.RFC3339Nano)), tc.count, false) + check("inside_window", mutate(prior, "@timestamp", time.Now().Add(-window+time.Minute).UTC().Format(time.RFC3339Nano)), tc.count, true) + for field := range terms { + if field == marker { + continue + } + check("different_"+field, mutate(prior, field, "other"), tc.count, false) + without := mutate(out, field, nil) + if yes, e := cache.Eval(r.Where, without); e != nil || yes { + t.Errorf("predicate accepts missing history identity %s: %v", field, e) + } + before := queries + if _, _, e := search.Execute(&without); e == nil { + t.Errorf("missing placeholder %s accepted", field) + } + if queries != before { + t.Error("missing placeholder executed query") + } + } + check("unrelated_population", mutate(prior, marker, nil), tc.count, false) + if tc.rule == "console_login_impossible_travel" { + check("same_country", mutate(prior, "origin.geolocation.countryCode", "US"), tc.count, false) + } + if tc.rule == "aws_golden_saml_attack" { + check("unrelated_same_account_activity", mutate(out, "@timestamp", time.Now().UTC().Format(time.RFC3339Nano)), 1, false) + } + if tc.rule == "secrets_manager_suspicious_access" { + if len(search.Or) != 1 || search.Or[0].Count != 5 || search.Or[0].Within != "10m" { + t.Fatal("batch secret OR threshold changed") + } + batch := mutate(prior, "log.eventName", "BatchGetSecretValue") + check("batch_below_threshold", batch, 4, false) + check("batch_or_threshold", batch, 5, true) + // Neither population independently reaches its threshold. + history = []string{} + for i := 0; i < 9; i++ { + history = append(history, prior) + } + for i := 0; i < 4; i++ { + history = append(history, batch) + } + if yes, _, e := search.Execute(&out); e != nil || yes { + t.Fatalf("mixed subthreshold history accepted: %v %v", yes, e) + } + } + }) + } + if len(cases) != 22 { + t.Fatalf("history coverage %d", len(cases)) + } +} diff --git a/plugins/alerts/testdata/aws_raw.json b/plugins/alerts/testdata/aws_raw.json new file mode 100644 index 000000000..c02e8cb40 --- /dev/null +++ b/plugins/alerts/testdata/aws_raw.json @@ -0,0 +1,2483 @@ +[ + { + "name": "backup-DeleteBackupVault", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"backup.amazonaws.com\",\"eventName\":\"DeleteBackupVault\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteBackupVault", + "action": "DeleteBackupVault", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_backup_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "backup-DeleteBackupVault-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"backup.amazonaws.com\",\"eventName\":\"DeleteBackupVault\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "config-StopConfigurationRecorder", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"config.amazonaws.com\",\"eventName\":\"StopConfigurationRecorder\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "StopConfigurationRecorder", + "action": "StopConfigurationRecorder", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_config_service_disabled", + "defense_evasion_configuration_recorder_stopped" + ] + }, + { + "name": "config-StopConfigurationRecorder-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"config.amazonaws.com\",\"eventName\":\"StopConfigurationRecorder\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "config-DeleteConfigRule", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"config.amazonaws.com\",\"eventName\":\"DeleteConfigRule\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteConfigRule", + "action": "DeleteConfigRule", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "defense_evasion_config_service_rule_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "config-DeleteConfigRule-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"config.amazonaws.com\",\"eventName\":\"DeleteConfigRule\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [ + "defense_evasion_config_service_rule_deletion" + ] + }, + { + "name": "ec2-ModifyInstanceAttribute", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifyInstanceAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"userData\":\"BASE64\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "userData": "BASE64" + }, + "log.eventName": "ModifyInstanceAttribute", + "action": "ModifyInstanceAttribute", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_ec2_startup_script_modification" + ] + }, + { + "name": "ec2-ModifyInstanceAttribute-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifyInstanceAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"userData\":\"BASE64\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "glue-CreateDevEndpoint", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"glue.amazonaws.com\",\"eventName\":\"CreateDevEndpoint\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "CreateDevEndpoint", + "action": "CreateDevEndpoint", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_glue_privilege_escalation" + ] + }, + { + "name": "glue-CreateDevEndpoint-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"glue.amazonaws.com\",\"eventName\":\"CreateDevEndpoint\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "sts-AssumeRoleWithSAML", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sts.amazonaws.com\",\"eventName\":\"AssumeRoleWithSAML\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"roleArn\":\"arn:aws:iam::123456789012:role/demo\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "roleArn": "arn:aws:iam::123456789012:role/demo" + }, + "log.eventName": "AssumeRoleWithSAML", + "action": "AssumeRoleWithSAML", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_golden_saml_attack" + ] + }, + { + "name": "sts-AssumeRoleWithSAML-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sts.amazonaws.com\",\"eventName\":\"AssumeRoleWithSAML\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"roleArn\":\"arn:aws:iam::123456789012:role/demo\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "iam-UpdateLoginProfile", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"UpdateLoginProfile\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"userName\":\"test-user\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "userName": "test-user" + }, + "log.eventName": "UpdateLoginProfile", + "action": "UpdateLoginProfile", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_iam_login_profile_modification" + ] + }, + { + "name": "iam-UpdateLoginProfile-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"UpdateLoginProfile\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"userName\":\"test-user\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "kms-ImportKeyMaterial", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"kms.amazonaws.com\",\"eventName\":\"ImportKeyMaterial\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "ImportKeyMaterial", + "action": "ImportKeyMaterial", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_kms_key_material_import" + ] + }, + { + "name": "kms-ImportKeyMaterial-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"kms.amazonaws.com\",\"eventName\":\"ImportKeyMaterial\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "rds-RestoreDBInstanceFromDBSnapshot", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"RestoreDBInstanceFromDBSnapshot\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"publiclyAccessible\":true},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "publiclyAccessible": true + }, + "log.eventName": "RestoreDBInstanceFromDBSnapshot", + "action": "RestoreDBInstanceFromDBSnapshot", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_rds_public_restore" + ] + }, + { + "name": "rds-RestoreDBInstanceFromDBSnapshot-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"RestoreDBInstanceFromDBSnapshot\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"publiclyAccessible\":true},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "s3-PutBucketVersioning", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketVersioning\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"VersioningConfiguration\":{\"Status\":\"Suspended\"}},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "VersioningConfiguration": { + "Status": "Suspended" + } + }, + "log.eventName": "PutBucketVersioning", + "action": "PutBucketVersioning", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_s3_versioning_disabled" + ] + }, + { + "name": "s3-PutBucketVersioning-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketVersioning\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"VersioningConfiguration\":{\"Status\":\"Suspended\"}},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "securityhub-BatchUpdateFindings", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"securityhub.amazonaws.com\",\"eventName\":\"BatchUpdateFindings\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "BatchUpdateFindings", + "action": "BatchUpdateFindings", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_securityhub_finding_evasion" + ] + }, + { + "name": "securityhub-BatchUpdateFindings-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"securityhub.amazonaws.com\",\"eventName\":\"BatchUpdateFindings\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-ModifySnapshotAttribute", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifySnapshotAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"snapshotId\":\"snap-test\",\"createVolumePermission\":{\"add\":{\"items\":[{\"group\":\"all\"}]}}},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "snapshotId": "snap-test", + "createVolumePermission": { + "add": { + "items": [ + { + "group": "all" + } + ] + } + } + }, + "log.eventName": "ModifySnapshotAttribute", + "action": "ModifySnapshotAttribute", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_snapshot_exfiltration", + "ebs_snapshot_sharing_violations", + "exfiltration_ec2_snapshot_change_activity" + ] + }, + { + "name": "ec2-ModifySnapshotAttribute-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifySnapshotAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"snapshotId\":\"snap-test\",\"createVolumePermission\":{\"add\":{\"items\":[{\"group\":\"all\"}]}}},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [ + "exfiltration_ec2_snapshot_change_activity" + ] + }, + { + "name": "ssm-SendCommand", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ssm.amazonaws.com\",\"eventName\":\"SendCommand\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"documentName\":\"AWS-RunShellScript\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "documentName": "AWS-RunShellScript" + }, + "log.eventName": "SendCommand", + "action": "SendCommand", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_ssm_sendcommand_abuse", + "ssm_session_abuse", + "initial_access_via_system_manager" + ] + }, + { + "name": "ssm-SendCommand-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ssm.amazonaws.com\",\"eventName\":\"SendCommand\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"documentName\":\"AWS-RunShellScript\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "sso-AssociateDirectory", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sso.amazonaws.com\",\"eventName\":\"AssociateDirectory\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "AssociateDirectory", + "action": "AssociateDirectory", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_sso_idp_change" + ] + }, + { + "name": "sso-AssociateDirectory-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sso.amazonaws.com\",\"eventName\":\"AssociateDirectory\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "sso-CreatePermissionSet", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sso.amazonaws.com\",\"eventName\":\"CreatePermissionSet\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "CreatePermissionSet", + "action": "CreatePermissionSet", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_sso_suspicious_activities" + ] + }, + { + "name": "sso-CreatePermissionSet-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sso.amazonaws.com\",\"eventName\":\"CreatePermissionSet\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "cloudformation-DeleteStack", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudformation.amazonaws.com\",\"eventName\":\"DeleteStack\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteStack", + "action": "DeleteStack", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "cloudformation_stack_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "cloudformation-DeleteStack-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudformation.amazonaws.com\",\"eventName\":\"DeleteStack\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "cloudtrail-PutEventSelectors", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"PutEventSelectors\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "PutEventSelectors", + "action": "PutEventSelectors", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "cloudtrail_event_selector_manipulation" + ] + }, + { + "name": "cloudtrail-PutEventSelectors-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"PutEventSelectors\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "cloudtrail-StopLogging", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"StopLogging\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "StopLogging", + "action": "StopLogging", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "cloudtrail_logging_disabled", + "defense_evasion_cloudtrail_logging_suspended" + ] + }, + { + "name": "cloudtrail-StopLogging-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"StopLogging\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "cloudtrail-DeleteTrail", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"DeleteTrail\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteTrail", + "action": "DeleteTrail", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "cloudtrail_logging_disabled", + "defense_evasion_cloudtrail_logging_deleted", + "mass_resource_deletion" + ] + }, + { + "name": "cloudtrail-DeleteTrail-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"DeleteTrail\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-RunInstances", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"RunInstances\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"instanceType\":\"p3.2xlarge\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "instanceType": "p3.2xlarge" + }, + "log.eventName": "RunInstances", + "action": "RunInstances", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "ec2_cryptomining_detection" + ] + }, + { + "name": "ec2-RunInstances-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"RunInstances\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"instanceType\":\"p3.2xlarge\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-ModifyInstanceMetadataOptions", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifyInstanceMetadataOptions\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"httpTokens\":\"optional\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "httpTokens": "optional" + }, + "log.eventName": "ModifyInstanceMetadataOptions", + "action": "ModifyInstanceMetadataOptions", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "ec2_metadata_abuse" + ] + }, + { + "name": "ec2-ModifyInstanceMetadataOptions-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifyInstanceMetadataOptions\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"httpTokens\":\"optional\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ecs-RegisterTaskDefinition", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ecs.amazonaws.com\",\"eventName\":\"RegisterTaskDefinition\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"containerDefinitions\":[{\"privileged\":true}]},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "containerDefinitions": [ + { + "privileged": true + } + ] + }, + "log.eventName": "RegisterTaskDefinition", + "action": "RegisterTaskDefinition", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "ecs_eks_container_abuse" + ] + }, + { + "name": "ecs-RegisterTaskDefinition-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ecs.amazonaws.com\",\"eventName\":\"RegisterTaskDefinition\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"containerDefinitions\":[{\"privileged\":true}]},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "iam-CreateUser", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"CreateUser\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "CreateUser", + "action": "CreateUser", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "iam_backdoor_creation_attempts" + ] + }, + { + "name": "iam-CreateUser-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"CreateUser\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "iam-AddUserToGroup", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"AddUserToGroup\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "AddUserToGroup", + "action": "AddUserToGroup", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "iam_privilege_escalation_paths" + ] + }, + { + "name": "iam-AddUserToGroup-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"AddUserToGroup\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "iam-AttachRolePolicy", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"AttachRolePolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"roleName\":\"review-role\",\"policyArn\":\"arn:aws:iam::aws:policy/AdministratorAccess\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "roleName": "review-role", + "policyArn": "arn:aws:iam::aws:policy/AdministratorAccess" + }, + "log.eventName": "AttachRolePolicy", + "action": "AttachRolePolicy", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "iam_privilege_escalation_paths", + "lambda_privilege_escalation" + ] + }, + { + "name": "iam-AttachRolePolicy-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"AttachRolePolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"roleName\":\"review-role\",\"policyArn\":\"arn:aws:iam::aws:policy/AdministratorAccess\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "lambda-CreateFunctionUrlConfig", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"lambda.amazonaws.com\",\"eventName\":\"CreateFunctionUrlConfig\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"authType\":\"NONE\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "authType": "NONE" + }, + "log.eventName": "CreateFunctionUrlConfig", + "action": "CreateFunctionUrlConfig", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "lambda_url_backdoor" + ] + }, + { + "name": "lambda-CreateFunctionUrlConfig-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"lambda.amazonaws.com\",\"eventName\":\"CreateFunctionUrlConfig\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"authType\":\"NONE\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "rds-AuthorizeDBSecurityGroupIngress", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"AuthorizeDBSecurityGroupIngress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"cidrip\":\"0.0.0.0/0\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "cidrip": "0.0.0.0/0" + }, + "log.eventName": "AuthorizeDBSecurityGroupIngress", + "action": "AuthorizeDBSecurityGroupIngress", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "rds_security_group_changes" + ] + }, + { + "name": "rds-AuthorizeDBSecurityGroupIngress-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"AuthorizeDBSecurityGroupIngress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"cidrip\":\"0.0.0.0/0\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "rds-ModifyDBSnapshotAttribute", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"ModifyDBSnapshotAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"attributeName\":\"restore\",\"valuesToAdd\":[\"111122223333\"]},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "attributeName": "restore", + "valuesToAdd": [ + "111122223333" + ] + }, + "log.eventName": "ModifyDBSnapshotAttribute", + "action": "ModifyDBSnapshotAttribute", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "rds_snapshot_exfiltration" + ] + }, + { + "name": "rds-ModifyDBSnapshotAttribute-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"ModifyDBSnapshotAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"attributeName\":\"restore\",\"valuesToAdd\":[\"111122223333\"]},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "route53-ChangeResourceRecordSets", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"route53.amazonaws.com\",\"eventName\":\"ChangeResourceRecordSets\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "ChangeResourceRecordSets", + "action": "ChangeResourceRecordSets", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "route53_dns_hijacking" + ] + }, + { + "name": "route53-ChangeResourceRecordSets-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"route53.amazonaws.com\",\"eventName\":\"ChangeResourceRecordSets\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "s3-PutBucketAcl", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketAcl\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"bucketName\":\"test-bucket\",\"x-amz-acl\":\"public-read\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "bucketName": "test-bucket", + "xamzacl": "public-read" + }, + "log.eventName": "PutBucketAcl", + "action": "PutBucketAcl", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "s3_bucket_public_exposure" + ] + }, + { + "name": "s3-PutBucketAcl-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketAcl\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"bucketName\":\"test-bucket\",\"x-amz-acl\":\"public-read\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "s3-GetObject", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"GetObject\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"bucketName\":\"test-bucket\",\"key\":\"test.txt\"},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "bucketName": "test-bucket", + "key": "test.txt" + }, + "log.eventName": "GetObject", + "action": "GetObject", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "s3_bulk_data_exfiltration" + ] + }, + { + "name": "s3-GetObject-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"GetObject\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"bucketName\":\"test-bucket\",\"key\":\"test.txt\"},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "secretsmanager-GetSecretValue", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"secretsmanager.amazonaws.com\",\"eventName\":\"GetSecretValue\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "GetSecretValue", + "action": "GetSecretValue", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "secrets_manager_suspicious_access" + ] + }, + { + "name": "secretsmanager-GetSecretValue-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"secretsmanager.amazonaws.com\",\"eventName\":\"GetSecretValue\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-AuthorizeSecurityGroupIngress", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"AuthorizeSecurityGroupIngress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"ipPermissions\":{\"items\":[{\"ipProtocol\":\"tcp\",\"ipRanges\":{\"items\":[{\"cidrIp\":\"0.0.0.0/0\"}]}}]}},\"responseElements\":null}", + "expected": { + "log.requestParameters": { + "ipPermissions": { + "items": [ + { + "ipProtocol": "tcp", + "ipRanges": { + "items": [ + { + "cidrIp": "0.0.0.0/0" + } + ] + } + } + ] + } + }, + "log.eventName": "AuthorizeSecurityGroupIngress", + "action": "AuthorizeSecurityGroupIngress", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "security_group_modifications" + ] + }, + { + "name": "ec2-AuthorizeSecurityGroupIngress-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"AuthorizeSecurityGroupIngress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"ipPermissions\":{\"items\":[{\"ipProtocol\":\"tcp\",\"ipRanges\":{\"items\":[{\"cidrIp\":\"0.0.0.0/0\"}]}}]}},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-DescribeNetworkAcls", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeNetworkAcls\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DescribeNetworkAcls", + "action": "DescribeNetworkAcls", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "unusual_api_call_patterns" + ] + }, + { + "name": "ec2-DescribeNetworkAcls-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeNetworkAcls\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-DeleteFlowLogs", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DeleteFlowLogs\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteFlowLogs", + "action": "DeleteFlowLogs", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "vpc_flow_log_anomalies", + "defense_evasion_ec2_flow_log_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "ec2-DeleteFlowLogs-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DeleteFlowLogs\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "wafv2-DeleteRule", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"wafv2.amazonaws.com\",\"eventName\":\"DeleteRule\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteRule", + "action": "DeleteRule", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "waf_shield_rule_modifications", + "defense_evasion_waf_rule_or_rule_group_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "wafv2-DeleteRule-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"wafv2.amazonaws.com\",\"eventName\":\"DeleteRule\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "guardduty-DeleteDetector", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"guardduty.amazonaws.com\",\"eventName\":\"DeleteDetector\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteDetector", + "action": "DeleteDetector", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "defense_evasion_guardduty_detector_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "guardduty-DeleteDetector-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"guardduty.amazonaws.com\",\"eventName\":\"DeleteDetector\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "s3-DeleteBucketPolicy", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"DeleteBucketPolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteBucketPolicy", + "action": "DeleteBucketPolicy", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "defense_evasion_s3_bucket_configuration_deletion" + ] + }, + { + "name": "s3-DeleteBucketPolicy-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"DeleteBucketPolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "wafv2-DeleteWebACL", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"wafv2.amazonaws.com\",\"eventName\":\"DeleteWebACL\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteWebACL", + "action": "DeleteWebACL", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "waf_shield_rule_modifications", + "defense_evasion_waf_acl_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "wafv2-DeleteWebACL-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"wafv2.amazonaws.com\",\"eventName\":\"DeleteWebACL\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-CreateTrafficMirrorFilter", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"CreateTrafficMirrorFilter\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "CreateTrafficMirrorFilter", + "action": "CreateTrafficMirrorFilter", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "exfiltration_ec2_full_network_packet_capture_detected" + ] + }, + { + "name": "ec2-CreateTrafficMirrorFilter-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"CreateTrafficMirrorFilter\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-CreateInstanceExportTask", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"CreateInstanceExportTask\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "CreateInstanceExportTask", + "action": "CreateInstanceExportTask", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "exfiltration_ec2_vm_export_failure" + ] + }, + { + "name": "ec2-CreateInstanceExportTask-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"CreateInstanceExportTask\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [ + "exfiltration_ec2_vm_export_failure" + ] + }, + { + "name": "rds-StartExportTask", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"StartExportTask\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "StartExportTask", + "action": "StartExportTask", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "exfiltration_rds_snapshot_export" + ] + }, + { + "name": "rds-StartExportTask-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"StartExportTask\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "cloudtrail-UpdateTrail", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"UpdateTrail\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "UpdateTrail", + "action": "UpdateTrail", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "impact_cloudtrail_logging_updated" + ] + }, + { + "name": "cloudtrail-UpdateTrail-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"cloudtrail.amazonaws.com\",\"eventName\":\"UpdateTrail\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "logs-DeleteLogGroup", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"logs.amazonaws.com\",\"eventName\":\"DeleteLogGroup\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteLogGroup", + "action": "DeleteLogGroup", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "impact_cloudwatch_log_group_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "logs-DeleteLogGroup-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"logs.amazonaws.com\",\"eventName\":\"DeleteLogGroup\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "logs-DeleteLogStream", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"logs.amazonaws.com\",\"eventName\":\"DeleteLogStream\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteLogStream", + "action": "DeleteLogStream", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "impact_cloudwatch_log_stream_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "logs-DeleteLogStream-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"logs.amazonaws.com\",\"eventName\":\"DeleteLogStream\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "ec2-DisableEbsEncryptionByDefault", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DisableEbsEncryptionByDefault\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DisableEbsEncryptionByDefault", + "action": "DisableEbsEncryptionByDefault", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "impact_ec2_disable_ebs_encryption" + ] + }, + { + "name": "ec2-DisableEbsEncryptionByDefault-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DisableEbsEncryptionByDefault\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "iam-DeactivateMFADevice", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"DeactivateMFADevice\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeactivateMFADevice", + "action": "DeactivateMFADevice", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "impact_iam_deactivate_mfa_device" + ] + }, + { + "name": "iam-DeactivateMFADevice-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"DeactivateMFADevice\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "rds-DeleteDBCluster", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"DeleteDBCluster\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "DeleteDBCluster", + "action": "DeleteDBCluster", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "impact_rds_cluster_deletion", + "mass_resource_deletion" + ] + }, + { + "name": "rds-DeleteDBCluster-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"DeleteDBCluster\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "signin-PasswordRecoveryRequested", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"PasswordRecoveryRequested\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "PasswordRecoveryRequested", + "action": "PasswordRecoveryRequested", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z" + }, + "absent": [], + "matches": [ + "initial_access_password_recovery" + ] + }, + { + "name": "signin-PasswordRecoveryRequested-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"PasswordRecoveryRequested\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [ + "initial_access_password_recovery" + ] + }, + { + "name": "route53domains-disableDomainTransferLock", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"route53domains.amazonaws.com\",\"eventName\":\"disableDomainTransferLock\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "disableDomainTransferLock", + "action": "disableDomainTransferLock", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "persistence_route_53_domain_transfer_lock_disabled" + ] + }, + { + "name": "route53domains-disableDomainTransferLock-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"route53domains.amazonaws.com\",\"eventName\":\"disableDomainTransferLock\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "route53domains-transferDomainToAnotherAwsAccount", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"route53domains.amazonaws.com\",\"eventName\":\"transferDomainToAnotherAwsAccount\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "transferDomainToAnotherAwsAccount", + "action": "transferDomainToAnotherAwsAccount", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "persistence_route_53_domain_transferred_to_another_account" + ] + }, + { + "name": "route53domains-transferDomainToAnotherAwsAccount-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"route53domains.amazonaws.com\",\"eventName\":\"transferDomainToAnotherAwsAccount\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "iam-UpdateAssumeRolePolicy", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"UpdateAssumeRolePolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "log.requestParameters": {}, + "log.eventName": "UpdateAssumeRolePolicy", + "action": "UpdateAssumeRolePolicy", + "origin.ip": "198.51.100.10", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "deviceTime": "2026-09-17T12:00:00Z", + "actionResult": "success" + }, + "absent": [], + "matches": [ + "privilege_escalation_updateassumerolepolicy" + ] + }, + { + "name": "iam-UpdateAssumeRolePolicy-denied", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"UpdateAssumeRolePolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"AccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [ + "privilege_escalation_updateassumerolepolicy" + ] + }, + { + "name": "root-Success-Yes", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"ConsoleLogin\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":{\"ConsoleLogin\":\"Success\"},\"additionalEventData\":{\"MFAUsed\":\"Yes\"}}", + "expected": { + "actionResult": "success", + "log.userIdentity.type": "Root" + }, + "absent": [], + "matches": [ + "initial_access_console_login_root" + ] + }, + { + "name": "root-Success-No", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"ConsoleLogin\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":{\"ConsoleLogin\":\"Success\"},\"additionalEventData\":{\"MFAUsed\":\"No\"}}", + "expected": { + "actionResult": "success", + "log.userIdentity.type": "Root" + }, + "absent": [], + "matches": [ + "initial_access_console_login_root", + "privilege_escalation_root_login_without_mfa", + "root_account_usage_without_mfa" + ] + }, + { + "name": "root-Success-None", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"ConsoleLogin\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":{\"ConsoleLogin\":\"Success\"}}", + "expected": { + "actionResult": "success", + "log.userIdentity.type": "Root" + }, + "absent": [], + "matches": [ + "initial_access_console_login_root" + ] + }, + { + "name": "root-Failure-Yes", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"ConsoleLogin\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":{\"ConsoleLogin\":\"Failure\"},\"additionalEventData\":{\"MFAUsed\":\"Yes\"},\"errorMessage\":\"Failed authentication\"}", + "expected": { + "actionResult": "failure", + "log.userIdentity.type": "Root" + }, + "absent": [], + "matches": [ + "credential_access_root_console_failure_brute_force" + ] + }, + { + "name": "root-Failure-No", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"ConsoleLogin\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":{\"ConsoleLogin\":\"Failure\"},\"additionalEventData\":{\"MFAUsed\":\"No\"},\"errorMessage\":\"Failed authentication\"}", + "expected": { + "actionResult": "failure", + "log.userIdentity.type": "Root" + }, + "absent": [], + "matches": [ + "credential_access_root_console_failure_brute_force" + ] + }, + { + "name": "root-Failure-None", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"ConsoleLogin\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":{\"ConsoleLogin\":\"Failure\"},\"errorMessage\":\"Failed authentication\"}", + "expected": { + "actionResult": "failure", + "log.userIdentity.type": "Root" + }, + "absent": [], + "matches": [ + "credential_access_root_console_failure_brute_force" + ] + }, + { + "name": "console-country-correlation", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"ConsoleLogin\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"ConsoleLogin\":\"Success\"}}", + "expected": { + "actionResult": "success" + }, + "absent": [], + "matches": [ + "console_login_impossible_travel" + ], + "enrichment": { + "origin.geolocation.countryCode": "US" + } + }, + { + "name": "GetSigninToken-Success", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"GetSigninToken\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"GetSigninToken\":\"Success\"}}", + "expected": { + "actionResult": "success" + }, + "absent": [], + "matches": [ + "aws_console_getsignintoken_abuse" + ] + }, + { + "name": "GetSigninToken-Failure", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"GetSigninToken\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"GetSigninToken\":\"Failure\"}}", + "expected": { + "actionResult": "failure" + }, + "absent": [], + "matches": [] + }, + { + "name": "CheckMfa-Success", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"CheckMfa\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"CheckMfa\":\"Success\"}}", + "expected": { + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "CheckMfa-Failure", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"signin.amazonaws.com\",\"eventName\":\"CheckMfa\",\"eventType\":\"AwsConsoleSignIn\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"CheckMfa\":\"Failure\"}}", + "expected": { + "actionResult": "failure" + }, + "absent": [], + "matches": [] + }, + { + "name": "ecs-assumed-role", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ecs.amazonaws.com\",\"eventName\":\"DescribeTaskDefinition\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"AssumedRole\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:sts::123456789012:assumed-role/task-role/session\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "aws_ecs_credential_theft" + ] + }, + { + "name": "malformed-trust-policy", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"UpdateAssumeRolePolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"MalformedPolicyDocument\"}", + "expected": { + "actionResult": "failure" + }, + "absent": [], + "matches": [ + "credential_access_aws_iam_assume_role_brute_force", + "privilege_escalation_updateassumerolepolicy" + ] + }, + { + "name": "trufflehog-user-agent", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"userAgent\":\"TruffleHog/1.0\"}", + "expected": {}, + "absent": [], + "matches": [ + "aws_trufflehog_scanning" + ] + }, + { + "name": "cross-account-role", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sts.amazonaws.com\",\"eventName\":\"AssumeRole\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"assumedRoleUser\":{\"arn\":\"arn:aws:sts::111122223333:assumed-role/demo/session\"}}}", + "expected": {}, + "absent": [], + "matches": [ + "cross_account_access_anomalies" + ] + }, + { + "name": "same-account-role", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sts.amazonaws.com\",\"eventName\":\"AssumeRole\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"assumedRoleUser\":{\"arn\":\"arn:aws:sts::123456789012:assumed-role/demo/session\"}}}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "sts-explicit-no-mfa", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sts.amazonaws.com\",\"eventName\":\"AssumeRole\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"false\"}}},\"requestParameters\":{},\"responseElements\":{\"assumedRoleUser\":{\"arn\":\"arn:aws:sts::123456789012:assumed-role/demo/session\"}}}", + "expected": {}, + "absent": [], + "matches": [ + "sts_token_abuse" + ] + }, + { + "name": "sts-mfa-missing-not-false", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sts.amazonaws.com\",\"eventName\":\"AssumeRole\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":{\"assumedRoleUser\":{\"arn\":\"arn:aws:sts::123456789012:assumed-role/demo/session\"}}}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "root-api-explicit-no-mfa", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"false\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "root_account_usage_without_mfa" + ] + }, + { + "name": "root-api-mfa-missing", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"Root\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:root\",\"principalId\":\"TESTPRINCIPAL\"},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-124", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"RestoreDBInstanceFromDBSnapshot\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"publiclyAccessible\":false},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-125", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketVersioning\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"VersioningConfiguration\":{\"Status\":\"Enabled\"}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-126", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifyInstanceAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"description\":\"mentions userData\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-127", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifySnapshotAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"createVolumePermission\":{\"remove\":{\"items\":[{\"group\":\"all\"}]}}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "exfiltration_ec2_snapshot_change_activity" + ] + }, + { + "name": "parameters-128", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifySnapshotAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"createVolumePermission\":{\"add\":{\"items\":[]}}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "exfiltration_ec2_snapshot_change_activity" + ] + }, + { + "name": "parameters-129", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"ModifyInstanceMetadataOptions\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"httpTokens\":\"required\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-130", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ecs.amazonaws.com\",\"eventName\":\"RegisterTaskDefinition\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"containerDefinitions\":[{\"privileged\":false}]},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-131", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ecs.amazonaws.com\",\"eventName\":\"RegisterTaskDefinition\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"volumes\":[{\"host\":{\"sourcePath\":\"/root\"}}]},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "ecs_eks_container_abuse" + ] + }, + { + "name": "parameters-132", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ecs.amazonaws.com\",\"eventName\":\"RegisterTaskDefinition\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"networkMode\":\"host\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "ecs_eks_container_abuse" + ] + }, + { + "name": "parameters-133", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"lambda.amazonaws.com\",\"eventName\":\"CreateFunctionUrlConfig\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"authType\":\"AWS_IAM\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-134", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"RevokeDBSecurityGroupIngress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"cidrip\":\"0.0.0.0/0\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-135", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"rds.amazonaws.com\",\"eventName\":\"ModifyDBSnapshotAttribute\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"attributeName\":\"restore\",\"valuesToRemove\":[\"all\"]},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-136", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketAcl\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"x-amz-acl\":\"private\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-137", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketPublicAccessBlock\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"PublicAccessBlockConfiguration\":{\"BlockPublicAcls\":false}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "s3_bucket_public_exposure" + ] + }, + { + "name": "parameters-138", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"PutBucketPublicAccessBlock\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"PublicAccessBlockConfiguration\":{\"BlockPublicAcls\":true,\"IgnorePublicAcls\":true,\"BlockPublicPolicy\":true,\"RestrictPublicBuckets\":true}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-139", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"s3.amazonaws.com\",\"eventName\":\"DeleteBucketPublicAccessBlock\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "s3_bucket_public_exposure" + ] + }, + { + "name": "parameters-140", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"AuthorizeSecurityGroupIngress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"ipPermissions\":{\"items\":[{\"ipRanges\":{\"items\":[{\"cidrIp\":\"10.0.0.0/8\"}]}}]}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-141", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"RevokeSecurityGroupIngress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"ipPermissions\":{\"items\":[{\"ipRanges\":{\"items\":[{\"cidrIp\":\"0.0.0.0/0\"}]}}]}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-142", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"AuthorizeSecurityGroupEgress\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"ipPermissions\":{\"items\":[{\"ipv6Ranges\":{\"items\":[{\"cidrIpv6\":\"::/0\"}]}}]}},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "security_group_modifications" + ] + }, + { + "name": "parameters-143", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"RunInstances\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"instanceType\":\"p30.madeup\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-144", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"RunInstances\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"instanceType\":\"c5.18xlarge\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "ec2_cryptomining_detection" + ] + }, + { + "name": "parameters-145", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"AttachRolePolicy\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"policyArn\":\"arn:aws:iam::123456789012:policy/AdministratorAccess-custom\"},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "iam_privilege_escalation_paths" + ] + }, + { + "name": "parameters-146", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"secretsmanager.amazonaws.com\",\"eventName\":\"BatchGetSecretValue\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [ + "secrets_manager_suspicious_access" + ] + }, + { + "name": "parameters-147", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"sso.amazonaws.com\",\"eventName\":\"ListInstances\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "parameters-148", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"route53.amazonaws.com\",\"eventName\":\"disableDomainTransferLock\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [], + "matches": [] + }, + { + "name": "outcome-149", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorMessage\":\"API failed\"}", + "expected": { + "actionResult": "failure" + }, + "absent": [], + "matches": [] + }, + { + "name": "outcome-150", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"errorCode\":\"AccessDeniedException\"}}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "outcome-151", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":{\"errorMessage\":\"Rejected\"}}", + "expected": { + "actionResult": "failure" + }, + "absent": [], + "matches": [] + }, + { + "name": "outcome-152", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsVpceEvent\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"VpceAccessDenied\"}", + "expected": { + "actionResult": "denied" + }, + "absent": [], + "matches": [] + }, + { + "name": "outcome-153", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsServiceEvent\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "outcome-154", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsVpceEvent\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "actionResult" + ], + "matches": [] + }, + { + "name": "outcome-155", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"errorCode\":\"\",\"errorMessage\":\"\"}", + "expected": { + "actionResult": "success" + }, + "absent": [], + "matches": [] + }, + { + "name": "source-0.0.0.0", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"0.0.0.0\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.ip", + "origin.host" + ], + "matches": [] + }, + { + "name": "source-::", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"::\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.ip", + "origin.host" + ], + "matches": [] + }, + { + "name": "source-0:0:0:0:0:0:0:0", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"0:0:0:0:0:0:0:0\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.ip", + "origin.host" + ], + "matches": [] + }, + { + "name": "source-AWS Internal/#", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"AWS Internal/#\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.ip", + "origin.host" + ], + "matches": [] + }, + { + "name": "source-ec2.amazonaws.com", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"ec2.amazonaws.com\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "origin.host": "ec2.amazonaws.com" + }, + "absent": [ + "origin.ip" + ], + "matches": [] + }, + { + "name": "source-2001:db8::1", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"2001:db8::1\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "origin.ip": "2001:db8::1" + }, + "absent": [ + "origin.host" + ], + "matches": [] + }, + { + "name": "source-192.0.2.10", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"192.0.2.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": { + "origin.ip": "192.0.2.10" + }, + "absent": [ + "origin.host" + ], + "matches": [] + }, + { + "name": "nested-retention", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{\"unknownVendorField\":{\"value\":\"retained\"}},\"responseElements\":null,\"additionalEventData\":{\"MFAUsed\":\"Yes\",\"customFlag\":true},\"tlsDetails\":{\"tlsVersion\":\"TLSv1.3\"}}", + "expected": { + "log.requestParameters.unknownVendorField.value": "retained", + "log.additionalEventData.customFlag": true, + "log.tlsDetails.tlsVersion": "TLSv1.3", + "log.tlsDetailsTlsVersion": "TLSv1.3" + }, + "absent": [], + "matches": [] + }, + { + "name": "input-markers-discarded", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"awsRecordType\":\"guardduty\",\"correlationCandidate\":{\"mass_resource_deletion\":\"match\"},\"awsActorKey\":\"fake\"}", + "expected": { + "log.awsRecordType": "cloudtrail", + "log.awsActorKey": "arn:aws:iam::123456789012:user/reviewer" + }, + "absent": [ + "log.correlationCandidate.mass_resource_deletion" + ], + "matches": [] + }, + { + "name": "guardduty-1", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":1}}", + "expected": { + "severity": "info" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [] + }, + { + "name": "guardduty-3.9", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":3.9}}", + "expected": { + "severity": "info" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [] + }, + { + "name": "guardduty-4", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":4}}", + "expected": { + "severity": "warning" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [] + }, + { + "name": "guardduty-6.9", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":6.9}}", + "expected": { + "severity": "warning" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [] + }, + { + "name": "guardduty-7", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":7}}", + "expected": { + "severity": "error" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [ + "guardduty_high_severity_findings" + ] + }, + { + "name": "guardduty-8.9", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":8.9}}", + "expected": { + "severity": "error" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [ + "guardduty_high_severity_findings" + ] + }, + { + "name": "guardduty-9", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":9}}", + "expected": { + "severity": "critical" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [ + "guardduty_high_severity_findings" + ] + }, + { + "name": "guardduty-10", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":10}}", + "expected": { + "severity": "critical" + }, + "absent": [ + "origin.ip", + "origin.user", + "actionResult" + ], + "matches": [ + "guardduty_high_severity_findings" + ] + }, + { + "name": "guardduty-11", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":11}}", + "expected": {}, + "absent": [ + "origin.ip", + "origin.user", + "actionResult", + "severity" + ], + "matches": [] + }, + { + "name": "guardduty--1", + "dataSource": "collector-test", + "raw": "{\"source\":\"aws.guardduty\",\"detail-type\":\"GuardDuty Finding\",\"account\":\"123456789012\",\"region\":\"us-east-1\",\"time\":\"2026-09-17T12:00:00Z\",\"detail\":{\"id\":\"finding-test\",\"type\":\"Recon:EC2/PortProbeUnprotectedPort\",\"severity\":-1}}", + "expected": {}, + "absent": [ + "origin.ip", + "origin.user", + "actionResult", + "severity" + ], + "matches": [] + }, + { + "name": "guardduty-management-is-not-finding", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"guardduty.amazonaws.com\",\"eventName\":\"ListFindings\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"severity\":9}", + "expected": {}, + "absent": [ + "severity" + ], + "matches": [] + }, + { + "name": "unknown-json", + "dataSource": "collector-test", + "raw": "{\"message\":\"not a CloudTrail record\",\"eventName\":\"DeleteTrail\",\"awsRecordType\":\"cloudtrail\",\"correlationCandidate\":{\"mass_resource_deletion\":\"match\"}}", + "expected": {}, + "absent": [ + "action", + "actionResult", + "log.awsRecordType", + "log.correlationCandidate" + ], + "matches": [] + }, + { + "name": "s3-archive-not-individual-event", + "dataSource": "collector-test", + "raw": "{\"Records\":[{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"CreateUser\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null},{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"iam.amazonaws.com\",\"eventName\":\"AddUserToGroup\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}]}", + "expected": {}, + "absent": [ + "action", + "actionResult", + "log.awsRecordType" + ], + "matches": [] + }, + { + "name": "invalid-user-178", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":null,\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "invalid-user-179", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":{},\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "invalid-user-180", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":[],\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "invalid-user-181", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":123,\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "invalid-user-182", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "invalid-user-183", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"-\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "invalid-user-184", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"HIDDEN_DUE_TO_SECURITY_REASONS\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null}", + "expected": {}, + "absent": [ + "origin.user" + ], + "matches": [] + }, + { + "name": "invalid-bytes-185", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"additionalEventData\":{\"bytesTransferredIn\":-1,\"bytesTransferredOut\":-1}}", + "expected": {}, + "absent": [ + "origin.bytesSent", + "origin.bytesReceived" + ], + "matches": [] + }, + { + "name": "invalid-bytes-186", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"additionalEventData\":{\"bytesTransferredIn\":\"NaN\",\"bytesTransferredOut\":\"NaN\"}}", + "expected": {}, + "absent": [ + "origin.bytesSent", + "origin.bytesReceived" + ], + "matches": [] + }, + { + "name": "invalid-bytes-187", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"additionalEventData\":{\"bytesTransferredIn\":\"Infinity\",\"bytesTransferredOut\":\"Infinity\"}}", + "expected": {}, + "absent": [ + "origin.bytesSent", + "origin.bytesReceived" + ], + "matches": [] + }, + { + "name": "invalid-bytes-188", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"additionalEventData\":{\"bytesTransferredIn\":{},\"bytesTransferredOut\":{}}}", + "expected": {}, + "absent": [ + "origin.bytesSent", + "origin.bytesReceived" + ], + "matches": [] + }, + { + "name": "invalid-bytes-189", + "dataSource": "collector-test", + "raw": "{\"eventVersion\":\"1.09\",\"eventTime\":\"2026-09-17T12:00:00Z\",\"eventSource\":\"ec2.amazonaws.com\",\"eventName\":\"DescribeTags\",\"eventType\":\"AwsApiCall\",\"awsRegion\":\"us-east-1\",\"sourceIPAddress\":\"198.51.100.10\",\"recipientAccountId\":\"123456789012\",\"userIdentity\":{\"type\":\"IAMUser\",\"accountId\":\"123456789012\",\"arn\":\"arn:aws:iam::123456789012:user/reviewer\",\"principalId\":\"TESTPRINCIPAL\",\"sessionContext\":{\"attributes\":{\"mfaAuthenticated\":\"true\"}}},\"requestParameters\":{},\"responseElements\":null,\"additionalEventData\":{\"bytesTransferredIn\":[],\"bytesTransferredOut\":[]}}", + "expected": {}, + "absent": [ + "origin.bytesSent", + "origin.bytesReceived" + ], + "matches": [] + } +] diff --git a/plugins/alerts/testdata/filter-contracts/aws.json b/plugins/alerts/testdata/filter-contracts/aws.json index 89bdd8992..cff5a4fb9 100644 --- a/plugins/alerts/testdata/filter-contracts/aws.json +++ b/plugins/alerts/testdata/filter-contracts/aws.json @@ -80,39 +80,105 @@ ], "fixtures": [ { - "name": "AWS principal/source/time promoted", + "name": "Denied AWS operation is not a completed change", "filter": "aws/aws.yml", "input": { + "dataType": "aws", "log": { - "sourceIPAddress": "198.51.100.10", - "eventTime": "2026-09-16T12:00:00Z", - "userIdentity": { - "arn": "arn:aws:iam::123456789012:user/alice" + "eventVersion": "1.09", + "eventSource": "cloudtrail.amazonaws.com", + "eventType": "AwsApiCall", + "eventName": "DeleteTrail", + "errorMessage": "API rejected", + "errorCode": "AccessDenied", + "awsRecordType": "forged", + "correlationCandidate": { + "mass_resource_deletion": "match" } } }, "expected": { - "origin.ip": "198.51.100.10", - "origin.user": "arn:aws:iam::123456789012:user/alice", - "deviceTime": "2026-09-16T12:00:00Z", - "log.userIdentityArn": "arn:aws:iam::123456789012:user/alice" + "actionResult": "denied", + "log.awsRecordType": "cloudtrail" }, - "absent": [], - "rules": {} - }, - { - "name": "AWS service name is not IP", - "filter": "aws/aws.yml", - "input": { - "log": { - "sourceIPAddress": "cloudtrail.amazonaws.com" - } - }, - "expected": {}, "absent": [ - "origin.ip" + "log.correlationCandidate" ], - "rules": {} + "rules": { + "rules/cloud/aws/aws/aws_backup_deletion.yml": false, + "rules/cloud/aws/aws/aws_config_service_disabled.yml": false, + "rules/cloud/aws/aws/aws_console_getsignintoken_abuse.yml": false, + "rules/cloud/aws/aws/aws_ec2_startup_script_modification.yml": false, + "rules/cloud/aws/aws/aws_ecs_credential_theft.yml": false, + "rules/cloud/aws/aws/aws_glue_privilege_escalation.yml": false, + "rules/cloud/aws/aws/aws_golden_saml_attack.yml": false, + "rules/cloud/aws/aws/aws_iam_login_profile_modification.yml": false, + "rules/cloud/aws/aws/aws_kms_key_material_import.yml": false, + "rules/cloud/aws/aws/aws_rds_public_restore.yml": false, + "rules/cloud/aws/aws/aws_s3_versioning_disabled.yml": false, + "rules/cloud/aws/aws/aws_securityhub_finding_evasion.yml": false, + "rules/cloud/aws/aws/aws_snapshot_exfiltration.yml": false, + "rules/cloud/aws/aws/aws_ssm_sendcommand_abuse.yml": false, + "rules/cloud/aws/aws/aws_sso_idp_change.yml": false, + "rules/cloud/aws/aws/aws_sso_suspicious_activities.yml": false, + "rules/cloud/aws/aws/aws_trufflehog_scanning.yml": false, + "rules/cloud/aws/aws/cloudformation_stack_deletion.yml": false, + "rules/cloud/aws/aws/cloudtrail_event_selector_manipulation.yml": false, + "rules/cloud/aws/aws/cloudtrail_logging_disabled.yml": false, + "rules/cloud/aws/aws/console_login_impossible_travel.yml": false, + "rules/cloud/aws/aws/cross_account_access_anomalies.yml": false, + "rules/cloud/aws/aws/ebs_snapshot_sharing_violations.yml": false, + "rules/cloud/aws/aws/ec2_cryptomining_detection.yml": false, + "rules/cloud/aws/aws/ec2_metadata_abuse.yml": false, + "rules/cloud/aws/aws/ecs_eks_container_abuse.yml": false, + "rules/cloud/aws/aws/guardduty_high_severity_findings.yml": false, + "rules/cloud/aws/aws/iam_backdoor_creation_attempts.yml": false, + "rules/cloud/aws/aws/iam_privilege_escalation_paths.yml": false, + "rules/cloud/aws/aws/lambda_privilege_escalation.yml": false, + "rules/cloud/aws/aws/lambda_url_backdoor.yml": false, + "rules/cloud/aws/aws/mass_resource_deletion.yml": false, + "rules/cloud/aws/aws/rds_security_group_changes.yml": false, + "rules/cloud/aws/aws/rds_snapshot_exfiltration.yml": false, + "rules/cloud/aws/aws/root_account_usage_without_mfa.yml": false, + "rules/cloud/aws/aws/route53_dns_hijacking.yml": false, + "rules/cloud/aws/aws/s3_bucket_public_exposure.yml": false, + "rules/cloud/aws/aws/s3_bulk_data_exfiltration.yml": false, + "rules/cloud/aws/aws/secrets_manager_suspicious_access.yml": false, + "rules/cloud/aws/aws/security_group_modifications.yml": false, + "rules/cloud/aws/aws/ssm_session_abuse.yml": false, + "rules/cloud/aws/aws/sts_token_abuse.yml": false, + "rules/cloud/aws/aws/unusual_api_call_patterns.yml": false, + "rules/cloud/aws/aws/vpc_flow_log_anomalies.yml": false, + "rules/cloud/aws/aws/waf_shield_rule_modifications.yml": false, + "rules/cloud/aws/credential_access_aws_iam_assume_role_brute_force.yml": false, + "rules/cloud/aws/credential_access_root_console_failure_brute_force.yml": false, + "rules/cloud/aws/defense_evasion_cloudtrail_logging_deleted.yml": false, + "rules/cloud/aws/defense_evasion_cloudtrail_logging_suspended.yml": false, + "rules/cloud/aws/defense_evasion_config_service_rule_deletion.yml": false, + "rules/cloud/aws/defense_evasion_configuration_recorder_stopped.yml": false, + "rules/cloud/aws/defense_evasion_ec2_flow_log_deletion.yml": false, + "rules/cloud/aws/defense_evasion_guardduty_detector_deletion.yml": false, + "rules/cloud/aws/defense_evasion_s3_bucket_configuration_deletion.yml": false, + "rules/cloud/aws/defense_evasion_waf_acl_deletion.yml": false, + "rules/cloud/aws/defense_evasion_waf_rule_or_rule_group_deletion.yml": false, + "rules/cloud/aws/exfiltration_ec2_full_network_packet_capture_detected.yml": false, + "rules/cloud/aws/exfiltration_ec2_snapshot_change_activity.yml": false, + "rules/cloud/aws/exfiltration_ec2_vm_export_failure.yml": false, + "rules/cloud/aws/exfiltration_rds_snapshot_export.yml": false, + "rules/cloud/aws/impact_cloudtrail_logging_updated.yml": false, + "rules/cloud/aws/impact_cloudwatch_log_group_deletion.yml": false, + "rules/cloud/aws/impact_cloudwatch_log_stream_deletion.yml": false, + "rules/cloud/aws/impact_ec2_disable_ebs_encryption.yml": false, + "rules/cloud/aws/impact_iam_deactivate_mfa_device.yml": false, + "rules/cloud/aws/impact_rds_cluster_deletion.yml": false, + "rules/cloud/aws/initial_access_console_login_root.yml": false, + "rules/cloud/aws/initial_access_password_recovery.yml": false, + "rules/cloud/aws/initial_access_via_system_manager.yml": false, + "rules/cloud/aws/persistence_route_53_domain_transfer_lock_disabled.yml": false, + "rules/cloud/aws/persistence_route_53_domain_transferred_to_another_account.yml": false, + "rules/cloud/aws/privilege_escalation_root_login_without_mfa.yml": false, + "rules/cloud/aws/privilege_escalation_updateassumerolepolicy.yml": false + } } ] } diff --git a/rules/cloud/aws/aws/aws_backup_deletion.yml b/rules/cloud/aws/aws/aws_backup_deletion.yml index 176573e74..c50d2f8ab 100644 --- a/rules/cloud/aws/aws/aws_backup_deletion.yml +++ b/rules/cloud/aws/aws/aws_backup_deletion.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Backup Deletion for Impact impact: confidentiality: 1 integrity: 3 availability: 3 category: Impact -technique: "T1490 - Inhibit System Recovery" +technique: T1490 - Inhibit System Recovery adversary: origin references: - - https://docs.aws.amazon.com/aws-backup/latest/devguide/API_Operations.html - - https://attack.mitre.org/techniques/T1490/ +- https://docs.aws.amazon.com/aws-backup/latest/devguide/API_Operations.html +- https://attack.mitre.org/techniques/T1490/ description: | Detects deletion of AWS Backup vaults, recovery points, or backup plans which could indicate an attacker attempting to prevent recovery after a destructive attack. Removing backups is a common precursor to ransomware or data destruction campaigns. @@ -26,9 +25,13 @@ description: | 7. Restore deleted recovery points from any cross-region or cross-account copies 8. Enable MFA delete on backup vaults and implement SCPs to restrict backup deletion where: | - equals("log.eventSource", "backup.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "backup.amazonaws.com") && oneOf("log.eventName", ["DeleteBackupVault", "DeleteRecoveryPoint", "DeleteBackupPlan", "DeleteBackupVaultAccessPolicy", "DeleteBackupVaultLockConfiguration"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_config_service_disabled.yml b/rules/cloud/aws/aws/aws_config_service_disabled.yml index cc6a066d1..dd928092a 100644 --- a/rules/cloud/aws/aws/aws_config_service_disabled.yml +++ b/rules/cloud/aws/aws/aws_config_service_disabled.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Config Service Disabled impact: confidentiality: 2 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562.008 - Impair Defenses: Disable Cloud Logs" +technique: 'T1562.008 - Impair Defenses: Disable Cloud Logs' adversary: origin references: - - https://docs.aws.amazon.com/config/latest/developerguide/stop-start-recorder.html - - https://attack.mitre.org/techniques/T1562/008/ +- https://docs.aws.amazon.com/config/latest/developerguide/stop-start-recorder.html +- https://attack.mitre.org/techniques/T1562/008/ description: | Detects attempts to disable AWS Config by stopping the configuration recorder or deleting the delivery channel. AWS Config tracks resource configuration changes and compliance, and disabling it is a common defense evasion technique. @@ -25,9 +24,13 @@ description: | 6. Review CloudTrail for API calls during the gap in Config monitoring 7. Verify compliance requirements are being met where: | - equals("log.eventSource", "config.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "config.amazonaws.com") && oneOf("log.eventName", ["DeleteDeliveryChannel", "StopConfigurationRecorder", "DeleteConfigurationRecorder"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_console_getsignintoken_abuse.yml b/rules/cloud/aws/aws/aws_console_getsignintoken_abuse.yml index e26b3b6e2..29f567a49 100644 --- a/rules/cloud/aws/aws/aws_console_getsignintoken_abuse.yml +++ b/rules/cloud/aws/aws/aws_console_getsignintoken_abuse.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Console GetSigninToken Abuse impact: confidentiality: 3 integrity: 2 availability: 1 category: Credential Access -technique: "T1550.001 - Use Alternate Authentication Material: Application Access Token" +technique: 'T1550.001 - Use Alternate Authentication Material: Application Access Token' adversary: origin references: - - https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html - - https://attack.mitre.org/techniques/T1550/001/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html +- https://attack.mitre.org/techniques/T1550/001/ description: | Detects GetSigninToken API calls which allow federated users to get a console sign-in token. Attackers abuse this to pivot from programmatic credentials to console access, potentially bypassing MFA requirements. @@ -24,9 +23,13 @@ description: | 5. Check if MFA was properly enforced on the original authentication 6. If unauthorized, revoke the federated session immediately where: | - equals("log.eventSource", "signin.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "signin.amazonaws.com") && equals("log.eventName", "GetSigninToken") && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.sourceIPAddress +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.sourceIPAddress diff --git a/rules/cloud/aws/aws/aws_ec2_startup_script_modification.yml b/rules/cloud/aws/aws/aws_ec2_startup_script_modification.yml index deafc3172..0aea40ace 100644 --- a/rules/cloud/aws/aws/aws_ec2_startup_script_modification.yml +++ b/rules/cloud/aws/aws/aws_ec2_startup_script_modification.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS EC2 Instance Startup Script Modified impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1059 - Command and Scripting Interpreter" +technique: T1059 - Command and Scripting Interpreter adversary: origin references: - - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html - - https://attack.mitre.org/techniques/T1059/ +- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html +- https://attack.mitre.org/techniques/T1059/ description: | Detects ModifyInstanceAttribute API calls targeting userData, which modifies the EC2 instance startup script. Attackers use this for persistent code execution with root/SYSTEM privileges on instance start. @@ -24,10 +23,14 @@ description: | 5. Examine the instance for signs of compromise 6. If unauthorized, remove the userData and investigate the instance where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "ModifyInstanceAttribute") && - equals("log.errorCode", "") && - contains("log.requestParameters", "userData") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ec2.amazonaws.com") && + oneOf("log.eventName",["ModifyInstanceAttribute"]) && + equals("actionResult","success") && + exists("log.requestParameters.userData")) groupBy: - - adversary.user - - lastEvent.log.requestParameters.instanceId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.instanceId diff --git a/rules/cloud/aws/aws/aws_ecs_credential_theft.yml b/rules/cloud/aws/aws/aws_ecs_credential_theft.yml index ec1265a9c..26a49b794 100644 --- a/rules/cloud/aws/aws/aws_ecs_credential_theft.yml +++ b/rules/cloud/aws/aws/aws_ecs_credential_theft.yml @@ -1,45 +1,55 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS ECS Task Credential Endpoint Query +- aws +name: Repeated ECS Task API Requests by an Assumed Role impact: confidentiality: 3 integrity: 2 availability: 1 -category: Credential Access -technique: "T1552.005 - Unsecured Credentials: Cloud Instance Metadata API" +category: System Integrity adversary: origin references: - - https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html - - https://attack.mitre.org/techniques/T1552/005/ +- https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html description: | - Detects queries to the ECS task credential endpoint that may indicate container credential theft. Attackers who compromise a container can query the credential endpoint to steal IAM role credentials attached to the ECS task. - - Next Steps: - 1. Identify the ECS task and cluster involved - 2. Check if the API calls were from expected task processes - 3. Review the IAM role attached to the task for excessive permissions - 4. Check for lateral movement using the stolen credentials - 5. Investigate the container image and running processes for compromise - 6. If unauthorized, rotate the task role credentials and investigate the container + Detects repeated successful DescribeTaskDefinition, RunTask or StartTask requests by an assumed role. These CloudTrail management calls do not establish access to the task credential endpoint or credential theft. Review the role, task and requested operations for authorization. where: | - equals("log.eventSource", "ecs.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ecs.amazonaws.com") && oneOf("log.eventName", ["DescribeTaskDefinition", "RunTask", "StartTask"]) && - equals("log.errorCode", "") && - equals("log.userIdentityType", "AssumedRole") && - contains("log.userIdentityArn", ":assumed-role/") + equals("actionResult","success") && + equals("log.userIdentity.type", "AssumedRole") && + contains("log.userIdentity.arn", ":assumed-role/"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - - field: log.eventSource - operator: filter_term - value: 'ecs.amazonaws.com' - within: 30m - count: 5 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.aws_ecs_credential_theft + operator: filter_term + value: match + within: 30m + count: 5 groupBy: - - adversary.user - - lastEvent.log.sourceIPAddress +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.sourceIPAddress diff --git a/rules/cloud/aws/aws/aws_glue_privilege_escalation.yml b/rules/cloud/aws/aws/aws_glue_privilege_escalation.yml index 7cb5c1ce2..3f6c63bca 100644 --- a/rules/cloud/aws/aws/aws_glue_privilege_escalation.yml +++ b/rules/cloud/aws/aws/aws_glue_privilege_escalation.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Glue Development Endpoint Privilege Escalation impact: confidentiality: 3 integrity: 3 availability: 2 category: Privilege Escalation -technique: "T1078.004 - Valid Accounts: Cloud Accounts" +technique: 'T1078.004 - Valid Accounts: Cloud Accounts' adversary: origin references: - - https://rhinosecuritylabs.com/aws/escalating-aws-iam-privileges-undocumented-codestar-api/ - - https://attack.mitre.org/techniques/T1078/004/ +- https://rhinosecuritylabs.com/aws/escalating-aws-iam-privileges-undocumented-codestar-api/ +- https://attack.mitre.org/techniques/T1078/004/ description: | Detects creation or modification of AWS Glue development endpoints which can be used for privilege escalation. An attacker can pass an IAM role to a Glue dev endpoint and then use it to execute code with that role's permissions, potentially escalating privileges. @@ -24,9 +23,13 @@ description: | 5. If unauthorized, delete the endpoint and investigate the passed role 6. Review IAM policies to restrict Glue PassRole permissions where: | - equals("log.eventSource", "glue.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "glue.amazonaws.com") && oneOf("log.eventName", ["CreateDevEndpoint", "UpdateDevEndpoint"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_golden_saml_attack.yml b/rules/cloud/aws/aws/aws_golden_saml_attack.yml index ce0332159..0ff0093b6 100644 --- a/rules/cloud/aws/aws/aws_golden_saml_attack.yml +++ b/rules/cloud/aws/aws/aws_golden_saml_attack.yml @@ -1,56 +1,50 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS Golden SAML Attack Detected +- aws +name: AWS SAML Role Assumption After Provider Change impact: confidentiality: 3 integrity: 3 availability: 3 category: Credential Access -technique: "T1556 - Modify Authentication Process" +technique: T1556 - Modify Authentication Process adversary: origin references: - - https://www.cyberark.com/resources/threat-research-blog/golden-saml-newly-discovered-attack-technique-forges-authentication-to-cloud-apps - - https://attack.mitre.org/techniques/T1556/ +- https://www.cyberark.com/resources/threat-research-blog/golden-saml-newly-discovered-attack-technique-forges-authentication-to-cloud-apps +- https://attack.mitre.org/techniques/T1556/ description: | - Detects potential Golden SAML attacks by monitoring for AssumeRoleWithSAML calls combined with UpdateSAMLProvider or CreateSAMLProvider operations. Golden SAML was the technique used in the SolarWinds attack, allowing attackers to forge SAML tokens and assume any role in the AWS account. - - Next Steps: - 1. Immediately investigate the SAML provider configuration changes and the AssumeRoleWithSAML calls - 2. Verify the SAML provider metadata document has not been tampered with - 3. Check if the identity provider federation trust is legitimate - 4. Review the assumed role ARN and session name for suspicious values - 5. Correlate with identity provider logs to verify the SAML assertion was legitimately issued - 6. Check for lateral movement or privilege escalation after the role assumption - 7. If unauthorized, rotate the SAML provider trust and revoke all active sessions + Detects a successful AssumeRoleWithSAML request after a successful SAML provider creation or update in the same AWS account and collector within 24 hours. The provider change can be by a different administrator. This sequence requires investigation and does not prove that a SAML assertion was forged. where: | - equals("log.eventSource", "sts.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "sts.amazonaws.com") && equals("log.eventName", "AssumeRoleWithSAML") && - equals("log.errorCode", "") + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityAccountId - operator: filter_term - value: '{{.log.userIdentityAccountId}}' - within: 24h - count: 1 - or: - - indexPattern: v11-log-aws-* - with: - - field: log.eventName - operator: filter_term - value: 'UpdateSAMLProvider' - within: 24h - count: 1 - - indexPattern: v11-log-aws-* - with: - - field: log.eventName - operator: filter_term - value: 'CreateSAMLProvider' - within: 24h - count: 1 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.correlationCandidate.saml_provider_change + operator: filter_term + value: match + within: 24h + count: 1 groupBy: - - adversary.user - - lastEvent.log.requestParametersRoleArn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParametersRoleArn diff --git a/rules/cloud/aws/aws/aws_iam_login_profile_modification.yml b/rules/cloud/aws/aws/aws_iam_login_profile_modification.yml index e44109dfd..a6ad8e66b 100644 --- a/rules/cloud/aws/aws/aws_iam_login_profile_modification.yml +++ b/rules/cloud/aws/aws/aws_iam_login_profile_modification.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS IAM Login Profile Modified for Another User impact: confidentiality: 3 integrity: 3 availability: 1 category: Persistence -technique: "T1098 - Account Manipulation" +technique: T1098 - Account Manipulation adversary: origin references: - - https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_admin-change-user.html - - https://attack.mitre.org/techniques/T1098/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_passwords_admin-change-user.html +- https://attack.mitre.org/techniques/T1098/ description: | Detects UpdateLoginProfile API calls where the modifier is potentially different from the target user. This can indicate an attacker changing another user's password to maintain access or enable console login for a compromised programmatic-only account. @@ -24,9 +23,13 @@ description: | 5. Check if the target user's MFA was also modified 6. If unauthorized, reset the password and rotate all credentials for the target user where: | - equals("log.eventSource", "iam.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "iam.amazonaws.com") && equals("log.eventName", "UpdateLoginProfile") && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.requestParameters.userName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.userName diff --git a/rules/cloud/aws/aws/aws_kms_key_material_import.yml b/rules/cloud/aws/aws/aws_kms_key_material_import.yml index cc2f58678..856912832 100644 --- a/rules/cloud/aws/aws/aws_kms_key_material_import.yml +++ b/rules/cloud/aws/aws/aws_kms_key_material_import.yml @@ -1,33 +1,25 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS KMS Key Material Import or Deletion impact: confidentiality: 3 integrity: 3 availability: 3 -category: Impact -technique: "T1486 - Data Encrypted for Impact" +category: System Integrity adversary: origin references: - - https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys.html - - https://attack.mitre.org/techniques/T1486/ +- https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys.html description: | - Detects ImportKeyMaterial or DeleteImportedKeyMaterial operations on AWS KMS. These are extremely rare operations that could indicate cloud ransomware preparation - an attacker importing their own key material to encrypt data and then deleting the imported key material to make decryption impossible without paying ransom. - - Next Steps: - 1. Immediately verify if this KMS key material operation was authorized - 2. Identify the KMS key ID affected and all resources encrypted with it - 3. Check the user identity and source IP for legitimacy - 4. Review if any data encryption operations followed this event - 5. Verify backup key material exists and is securely stored - 6. If unauthorized, immediately disable the KMS key and investigate data integrity - 7. Check for associated DeleteImportedKeyMaterial calls that would prevent decryption + Identifies successful ImportKeyMaterial or DeleteImportedKeyMaterial requests. Verify the key, authorization and dependent resources. These are key-management operations and do not by themselves prove encryption of data or ransomware. Review key lifecycle and recovery plans before taking any containment action. where: | - equals("log.eventSource", "kms.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "kms.amazonaws.com") && oneOf("log.eventName", ["ImportKeyMaterial", "DeleteImportedKeyMaterial"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_rds_public_restore.yml b/rules/cloud/aws/aws/aws_rds_public_restore.yml index 134a58910..6665d3c76 100644 --- a/rules/cloud/aws/aws/aws_rds_public_restore.yml +++ b/rules/cloud/aws/aws/aws_rds_public_restore.yml @@ -1,33 +1,26 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS RDS Database Restored as Publicly Accessible +- aws +name: AWS RDS Publicly Accessible Restore Requested impact: confidentiality: 3 integrity: 2 availability: 1 -category: Exfiltration -technique: "T1020 - Automated Exfiltration" +category: System Integrity adversary: origin references: - - https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_RestoreFromSnapshot.html - - https://attack.mitre.org/techniques/T1020/ +- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_RestoreFromSnapshot.html description: | - Detects RestoreDBInstanceFromDBSnapshot or RestoreDBClusterFromSnapshot API calls that may create publicly accessible database instances. Attackers use this to exfiltrate database contents by restoring a snapshot as a publicly accessible instance. - - Next Steps: - 1. Verify the database restore was authorized and part of an approved workflow - 2. Check if the restored instance is publicly accessible - 3. Review the security group and subnet configuration of the restored instance - 4. Identify the source snapshot and its data sensitivity - 5. If publicly accessible and unauthorized, immediately modify the instance to private - 6. Check for any connections to the restored instance from external IPs + Identifies a successful database restore request explicitly setting publiclyAccessible to true. Verify the task status, subnet routing, security groups and approval. Request acceptance does not prove that the database is reachable from the internet or that any data was exfiltrated. where: | - equals("log.eventSource", "rds.amazonaws.com") && - oneOf("log.eventName", ["RestoreDBInstanceFromDBSnapshot", "RestoreDBClusterFromSnapshot"]) && - equals("log.errorCode", "") && - contains("log.requestParameters", "publiclyAccessible") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","rds.amazonaws.com") && + oneOf("log.eventName",["RestoreDBInstanceFromDBSnapshot", "RestoreDBClusterFromSnapshot"]) && + equals("actionResult","success") && + equals("log.requestParameters.publiclyAccessible",true)) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_s3_versioning_disabled.yml b/rules/cloud/aws/aws/aws_s3_versioning_disabled.yml index b985243db..cabf08479 100644 --- a/rules/cloud/aws/aws/aws_s3_versioning_disabled.yml +++ b/rules/cloud/aws/aws/aws_s3_versioning_disabled.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS S3 Bucket Versioning Suspended impact: confidentiality: 2 integrity: 3 availability: 3 category: Impact -technique: "T1490 - Inhibit System Recovery" +technique: T1490 - Inhibit System Recovery adversary: origin references: - - https://docs.aws.amazon.com/AmazonS3/latest/userguide/Versioning.html - - https://attack.mitre.org/techniques/T1490/ +- https://docs.aws.amazon.com/AmazonS3/latest/userguide/Versioning.html +- https://attack.mitre.org/techniques/T1490/ description: | Detects when S3 bucket versioning is suspended via PutBucketVersioning. Disabling versioning is a cloud ransomware preparation technique that prevents recovery of overwritten or deleted objects from version history. @@ -25,10 +24,15 @@ description: | 6. Check if MFA Delete was configured on the bucket 7. Review S3 bucket lifecycle policies for any recent changes where: | - equals("log.eventSource", "s3.amazonaws.com") && - equals("log.eventName", "PutBucketVersioning") && - equals("log.errorCode", "") && - contains("log.requestParameters", "Suspended") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","s3.amazonaws.com") && + oneOf("log.eventName",["PutBucketVersioning"]) && + equals("actionResult","success") && + (equals("log.requestParameters.VersioningConfiguration.Status","Suspended") || + equals("log.requestParameters.versioningConfiguration.status","Suspended"))) groupBy: - - adversary.user - - lastEvent.log.requestParameters.bucketName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.bucketName diff --git a/rules/cloud/aws/aws/aws_securityhub_finding_evasion.yml b/rules/cloud/aws/aws/aws_securityhub_finding_evasion.yml index ff0058b4a..93235b5b2 100644 --- a/rules/cloud/aws/aws/aws_securityhub_finding_evasion.yml +++ b/rules/cloud/aws/aws/aws_securityhub_finding_evasion.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS SecurityHub Finding Evasion impact: confidentiality: 2 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562 - Impair Defenses" +technique: T1562 - Impair Defenses adversary: origin references: - - https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings.html - - https://attack.mitre.org/techniques/T1562/ +- https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings.html +- https://attack.mitre.org/techniques/T1562/ description: | Detects attempts to suppress or manipulate AWS SecurityHub findings through BatchUpdateFindings, DeleteInsight, or UpdateFindings operations. Attackers use these to hide evidence of their activities from security monitoring. @@ -25,21 +24,47 @@ description: | 6. Restore suppressed findings if the action was unauthorized 7. Review SecurityHub aggregation and automation rules for tampering where: | - equals("log.eventSource", "securityhub.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "securityhub.amazonaws.com") && oneOf("log.eventName", ["BatchUpdateFindings", "DeleteInsight", "UpdateFindings", "DeleteActionTarget"]) && - equals("log.errorCode", "") && - !equals("log.userIdentityType", "AWSService") + equals("actionResult","success") && + !equals("log.userIdentity.type", "AWSService"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventSource - operator: filter_term - value: 'securityhub.amazonaws.com' - within: 30m - count: 5 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.aws_securityhub_finding_evasion + operator: filter_term + value: match + within: 30m + count: 5 groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_snapshot_exfiltration.yml b/rules/cloud/aws/aws/aws_snapshot_exfiltration.yml index 9a5c60e47..7c04b4c99 100644 --- a/rules/cloud/aws/aws/aws_snapshot_exfiltration.yml +++ b/rules/cloud/aws/aws/aws_snapshot_exfiltration.yml @@ -1,34 +1,33 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS EC2 Snapshot Shared with External Account +- aws +name: AWS EBS Snapshot Sharing Permission Added impact: confidentiality: 3 integrity: 2 availability: 1 category: Exfiltration -technique: "T1537 - Transfer Data to Cloud Account" +technique: T1537 - Transfer Data to Cloud Account adversary: origin references: - - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-modifying-snapshot-permissions.html - - https://attack.mitre.org/techniques/T1537/ +- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-modifying-snapshot-permissions.html +- https://attack.mitre.org/techniques/T1537/ description: | - Detects ModifySnapshotAttribute API calls that share EBS snapshots with external AWS accounts. Attackers exfiltrate data by sharing snapshots containing sensitive data with accounts they control. - - Next Steps: - 1. Identify the shared snapshot and its contents - 2. Verify the target account ID is a known and authorized account - 3. Check the user identity and source IP for legitimacy - 4. Review the snapshot's source volume for sensitive data - 5. If unauthorized, immediately remove the sharing permission - 6. Check for other snapshots shared around the same time - 7. Review IAM policies to restrict snapshot sharing + Detects a successful ModifySnapshotAttribute request that adds volume-creation permissions. Removal-only changes do not match. Review the added accounts or public group; sharing permission does not establish completed data exfiltration. where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "ModifySnapshotAttribute") && - equals("log.errorCode", "") && - contains("log.requestParameters", "createVolumePermission") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ec2.amazonaws.com") && + oneOf("log.eventName",["ModifySnapshotAttribute"]) && + equals("actionResult","success") && + (greaterThan("log.requestParameters.createVolumePermission.add.items.#",0) || + greaterThan("log.requestParameters.createVolumePermission.add.#",0) || + (equals("log.requestParameters.attribute","createVolumePermission") && + equals("log.requestParameters.operationType","add") && + (greaterThan("log.requestParameters.userIds.#",0) || + greaterThan("log.requestParameters.userGroups.#",0))))) groupBy: - - adversary.user - - lastEvent.log.requestParameters.snapshotId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.snapshotId diff --git a/rules/cloud/aws/aws/aws_ssm_sendcommand_abuse.yml b/rules/cloud/aws/aws/aws_ssm_sendcommand_abuse.yml index a535f49b1..9d215057e 100644 --- a/rules/cloud/aws/aws/aws_ssm_sendcommand_abuse.yml +++ b/rules/cloud/aws/aws/aws_ssm_sendcommand_abuse.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS SSM SendCommand Remote Execution impact: confidentiality: 2 integrity: 3 availability: 2 category: Execution -technique: "T1059 - Command and Scripting Interpreter" +technique: T1059 - Command and Scripting Interpreter adversary: origin references: - - https://docs.aws.amazon.com/systems-manager/latest/userguide/run-command.html - - https://attack.mitre.org/techniques/T1059/ +- https://docs.aws.amazon.com/systems-manager/latest/userguide/run-command.html +- https://attack.mitre.org/techniques/T1059/ description: | Detects AWS Systems Manager SendCommand API calls which allow remote command execution on EC2 instances. Attackers with sufficient IAM permissions abuse SSM to execute commands across multiple instances without SSH/RDP access. @@ -24,20 +23,46 @@ description: | 5. Correlate with instance-level logs for the executed commands 6. If unauthorized, cancel pending commands and investigate the user where: | - equals("log.eventSource", "ssm.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ssm.amazonaws.com") && oneOf("log.eventName", ["SendCommand", "StartSession"]) && - equals("log.errorCode", "") + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventSource - operator: filter_term - value: 'ssm.amazonaws.com' - within: 30m - count: 5 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.aws_ssm_sendcommand_abuse + operator: filter_term + value: match + within: 30m + count: 5 groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_sso_idp_change.yml b/rules/cloud/aws/aws/aws_sso_idp_change.yml index dbc5b0c5a..4dcde006c 100644 --- a/rules/cloud/aws/aws/aws_sso_idp_change.yml +++ b/rules/cloud/aws/aws/aws_sso_idp_change.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS SSO Identity Provider Configuration Changed impact: confidentiality: 3 integrity: 3 availability: 2 category: Credential Access -technique: "T1556 - Modify Authentication Process" +technique: T1556 - Modify Authentication Process adversary: origin references: - - https://docs.aws.amazon.com/singlesignon/latest/userguide/manage-your-identity-source.html - - https://attack.mitre.org/techniques/T1556/ +- https://docs.aws.amazon.com/singlesignon/latest/userguide/manage-your-identity-source.html +- https://attack.mitre.org/techniques/T1556/ description: | Detects changes to AWS SSO identity provider configuration including directory association and external IdP configuration changes. Modifying the identity provider can enable impersonation of any user in the organization. @@ -25,9 +24,13 @@ description: | 6. If unauthorized, revert the IdP configuration immediately 7. Audit all SSO-based sessions created after the change where: | - equals("log.eventSource", "sso.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "sso.amazonaws.com") && oneOf("log.eventName", ["AssociateDirectory", "DisassociateDirectory", "EnableExternalIdPConfiguration", "DisableExternalIdPConfiguration", "UpdateExternalIdPConfiguration"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/aws_sso_suspicious_activities.yml b/rules/cloud/aws/aws/aws_sso_suspicious_activities.yml index 1b14a9625..0421391d3 100644 --- a/rules/cloud/aws/aws/aws_sso_suspicious_activities.yml +++ b/rules/cloud/aws/aws/aws_sso_suspicious_activities.yml @@ -1,45 +1,55 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS SSO Suspicious Activities +- aws +name: Repeated AWS SSO Permission Set Changes impact: confidentiality: 3 integrity: 3 availability: 1 -category: Credential Access, Defense Evasion, Persistence -technique: "T1556.006 - Modify Authentication Process: Multi-Factor Authentication" +category: Privilege Escalation adversary: origin references: - - https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html - - https://attack.mitre.org/techniques/T1556/006/ +- https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html +- https://attack.mitre.org/techniques/T1098/ description: | - Detects suspicious AWS SSO activities including multiple failed login attempts, unusual permission set assignments, or SSO configuration changes that could indicate an attempt to compromise single sign-on authentication mechanisms. - - Next Steps: - 1. Review the source IP address and user identity associated with the suspicious SSO activity - 2. Check if the user's credentials may have been compromised - 3. Verify if the permission set changes or role assumptions were authorized - 4. Review CloudTrail logs for other suspicious activities from the same IP or user - 5. If unauthorized, immediately revoke the affected SSO sessions and reset user credentials - 6. Consider implementing additional MFA requirements for SSO administrative actions + Detects repeated successful permission-set creation, policy attachment, deletion or inline-policy updates in AWS SSO from the same source and account. These administrative operations do not establish a failed login or compromised SSO session. where: | - equals("log.eventSource", "sso.amazonaws.com") && - ( - oneOf("log.eventName", ["AssumeRoleWithSAML", "CreatePermissionSet", "AttachManagedPolicyToPermissionSet", "DeletePermissionSet", "PutInlinePolicyToPermissionSet"]) || - exists("log.errorCode") - ) + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","sso.amazonaws.com") && + oneOf("log.eventName",["CreatePermissionSet", "AttachManagedPolicyToPermissionSet", "DeletePermissionSet", "PutInlinePolicyToPermissionSet"]) && + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - - field: log.eventSource - operator: filter_term - value: 'sso.amazonaws.com' - within: 30m - count: 10 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.aws_sso_suspicious_activities + operator: filter_term + value: match + within: 30m + count: 10 groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentity.principalId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentity.principalId +technique: T1098 - Account Manipulation diff --git a/rules/cloud/aws/aws/aws_trufflehog_scanning.yml b/rules/cloud/aws/aws/aws_trufflehog_scanning.yml index 62b7409a8..e483233cf 100644 --- a/rules/cloud/aws/aws/aws_trufflehog_scanning.yml +++ b/rules/cloud/aws/aws/aws_trufflehog_scanning.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS TruffleHog Credential Scanning Detected impact: confidentiality: 3 integrity: 1 availability: 0 category: Credential Access -technique: "T1552 - Unsecured Credentials" +technique: T1552 - Unsecured Credentials adversary: origin references: - - https://github.com/trufflesecurity/trufflehog - - https://attack.mitre.org/techniques/T1552/ +- https://github.com/trufflesecurity/trufflehog +- https://attack.mitre.org/techniques/T1552/ description: | Detects TruffleHog credential scanning tool activity in AWS CloudTrail logs identified by its characteristic user agent string. TruffleHog is used to scan for exposed secrets and credentials in AWS environments. @@ -24,8 +23,12 @@ description: | 5. Check for any credentials that may have been discovered and exfiltrated 6. Review IAM policies to limit enumeration permissions where: | - contains("log.userAgent", "trufflehog") || - contains("log.userAgent", "TruffleHog") + equals("log.awsRecordType","cloudtrail") && + (contains("log.userAgent", "trufflehog") || + contains("log.userAgent", "TruffleHog")) groupBy: - - adversary.user - - lastEvent.log.sourceIPAddress +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.sourceIPAddress diff --git a/rules/cloud/aws/aws/cloudformation_stack_deletion.yml b/rules/cloud/aws/aws/cloudformation_stack_deletion.yml index e44a89e5d..aef371803 100644 --- a/rules/cloud/aws/aws/cloudformation_stack_deletion.yml +++ b/rules/cloud/aws/aws/cloudformation_stack_deletion.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS CloudFormation Stack Deletion impact: confidentiality: 1 integrity: 3 availability: 3 category: Impact -technique: "T1485 - Data Destruction" +technique: T1485 - Data Destruction adversary: origin references: - - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/logging-cloudformation-api-calls.html - - https://attack.mitre.org/techniques/T1485/ +- https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/logging-cloudformation-api-calls.html +- https://attack.mitre.org/techniques/T1485/ description: | Detects deletion of CloudFormation stacks which could indicate destructive actions by an attacker or unauthorized infrastructure changes. This rule monitors for DeleteStack operations that could result in loss of critical infrastructure. Multiple stack deletions from the same user within a short time frame may indicate malicious activity. - + Next Steps: 1. Verify if the stack deletion was authorized and part of planned maintenance 2. Check the user identity who performed the deletion and validate if they should have this permission @@ -24,18 +23,47 @@ description: | 5. If unauthorized, immediately revoke the user's permissions and investigate for other compromise indicators 6. Consider enabling stack termination protection on critical stacks 7. Review and implement least privilege access policies for CloudFormation operations -where: equals("log.eventSource", "cloudformation.amazonaws.com") && equals("log.eventName", "DeleteStack") && equals("log.errorCode", "") +where: | + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "cloudformation.amazonaws.com") && + equals("log.eventName", "DeleteStack") && + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventSource - operator: filter_term - value: 'cloudformation.amazonaws.com' - within: 30m - count: 5 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.cloudformation_stack_deletion + operator: filter_term + value: match + within: 30m + count: 5 groupBy: - - lastEvent.log.userIdentityAccountId - - lastEvent.log.userIdentityArn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.userIdentityAccountId +- lastEvent.log.userIdentityArn diff --git a/rules/cloud/aws/aws/cloudtrail_event_selector_manipulation.yml b/rules/cloud/aws/aws/cloudtrail_event_selector_manipulation.yml index 4c677e846..62bac2ec6 100644 --- a/rules/cloud/aws/aws/cloudtrail_event_selector_manipulation.yml +++ b/rules/cloud/aws/aws/cloudtrail_event_selector_manipulation.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS CloudTrail Event Selector Manipulation impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562.008 - Impair Defenses: Disable Cloud Logs" +technique: 'T1562.008 - Impair Defenses: Disable Cloud Logs' adversary: origin references: - - https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html - - https://attack.mitre.org/techniques/T1562/008/ +- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html +- https://attack.mitre.org/techniques/T1562/008/ description: | Detects manipulation of CloudTrail event selectors which can be used to selectively exclude data events from logging. Attackers may use PutEventSelectors to disable logging of S3, Lambda, or DynamoDB data events to hide their data exfiltration or manipulation activities. @@ -26,9 +25,13 @@ description: | 7. Implement SCPs to prevent modification of CloudTrail event selectors 8. Enable AWS Config rules to monitor CloudTrail configuration compliance where: | - equals("log.eventSource", "cloudtrail.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "cloudtrail.amazonaws.com") && oneOf("log.eventName", ["PutEventSelectors", "PutInsightSelectors"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.requestParameters.trailName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.trailName diff --git a/rules/cloud/aws/aws/cloudtrail_logging_disabled.yml b/rules/cloud/aws/aws/cloudtrail_logging_disabled.yml index ce8bcf591..6a5a1853f 100644 --- a/rules/cloud/aws/aws/cloudtrail_logging_disabled.yml +++ b/rules/cloud/aws/aws/cloudtrail_logging_disabled.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS CloudTrail Logging Disabled impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562.008 - Impair Defenses: Disable Cloud Logs" +technique: 'T1562.008 - Impair Defenses: Disable Cloud Logs' adversary: origin references: - - https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html - - https://attack.mitre.org/techniques/T1562/008/ +- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html +- https://attack.mitre.org/techniques/T1562/008/ description: | Detects attempts to disable CloudTrail logging which could be used to hide malicious activities and evade detection. CloudTrail provides audit logs of all AWS API calls and is critical for security monitoring and compliance. - + Next Steps: 1. Immediately verify if the CloudTrail modification was authorized 2. Check the user identity ({{log.userIdentityArn}}) and source IP ({{log.sourceIPAddress}}) for legitimacy @@ -28,9 +27,13 @@ description: | 9. Review AWS Config rules for CloudTrail compliance 10. Enable CloudTrail Insights for anomaly detection where: | - equals("log.eventSource", "cloudtrail.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "cloudtrail.amazonaws.com") && oneOf("log.eventName", ["StopLogging", "DeleteTrail"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.requestParameters.name +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.name diff --git a/rules/cloud/aws/aws/console_login_impossible_travel.yml b/rules/cloud/aws/aws/console_login_impossible_travel.yml index ffe6be2f8..d6f52837d 100644 --- a/rules/cloud/aws/aws/console_login_impossible_travel.yml +++ b/rules/cloud/aws/aws/console_login_impossible_travel.yml @@ -1,48 +1,64 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS Console Login Impossible Travel Detection +- aws +name: AWS Console Logins from Different Countries impact: confidentiality: 3 integrity: 2 availability: 1 category: Credential Access -technique: "T1078.004 - Valid Accounts: Cloud Accounts" +technique: 'T1078.004 - Valid Accounts: Cloud Accounts' adversary: origin references: - - https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html - - https://attack.mitre.org/techniques/T1078/004/ +- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html +- https://attack.mitre.org/techniques/T1078/004/ description: | - Detects AWS Console logins from different geographic locations within a short time window, indicating potential credential compromise. This correlation identifies when the same user authenticates from different countries within 30 minutes, which would be physically impossible. - - Next Steps: - 1. Review the login locations and IP addresses for the affected user - 2. Check if the user employs a VPN or proxy service that could explain different geolocations - 3. Verify with the user whether both login sessions are legitimate - 4. Review the actions performed in each session for suspicious activity - 5. If unauthorized, immediately disable the user account and rotate credentials - 6. Enable MFA if not already configured for the affected account - 7. Review CloudTrail logs for any API calls made during the suspicious session - 8. Check for concurrent sessions from the compromised account + Detects successful console logins by the same principal and account from different geolocated countries within 30 minutes. VPNs, proxies and geolocation changes can produce this sequence; it does not establish impossible travel. Verify the sign-ins with the account owner. where: | - equals("log.eventName", "ConsoleLogin") && - equals("log.responseElements.ConsoleLogin", "Success") && - exists("origin.geolocation.countryCode") + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","signin.amazonaws.com") && + oneOf("log.eventName",["ConsoleLogin"]) && + equals("actionResult","success") && + regexMatch("origin.geolocation.countryCode","^[A-Z]{2}$"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventName - operator: filter_term - value: ConsoleLogin - - field: origin.geolocation.countryCode - operator: must_not_term - value: '{{.origin.geolocation.countryCode}}' - within: 30m - count: 1 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.console_login_impossible_travel + operator: filter_term + value: match + - field: origin.geolocation.countryCode + operator: must_not_term + value: '{{.origin.geolocation.countryCode}}' + within: 30m + count: 1 groupBy: - - adversary.user - - adversary.geolocation.countryCode +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- adversary.geolocation.countryCode diff --git a/rules/cloud/aws/aws/cross_account_access_anomalies.yml b/rules/cloud/aws/aws/cross_account_access_anomalies.yml index 2443f165c..cfe5008a4 100644 --- a/rules/cloud/aws/aws/cross_account_access_anomalies.yml +++ b/rules/cloud/aws/aws/cross_account_access_anomalies.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Cross-Account Access Anomalies impact: confidentiality: 3 integrity: 3 availability: 1 category: Unauthorized Access -technique: "T1550.001 - Use Alternate Authentication Material: Application Access Token" +technique: 'T1550.001 - Use Alternate Authentication Material: Application Access Token' adversary: origin references: - - https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html - - https://attack.mitre.org/techniques/T1550/001/ +- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html +- https://attack.mitre.org/techniques/T1550/001/ description: | Detects anomalous cross-account access patterns in AWS that may indicate account compromise or privilege escalation. Monitors for AssumeRole activities across different accounts where the assumed role ARN does not match the originating account ID, potentially indicating unauthorized cross-account access. - + Next Steps: 1. Verify if the cross-account access was authorized by checking AWS IAM policies and trust relationships 2. Review the source IP address to determine if it matches known corporate IP ranges or expected locations @@ -24,25 +23,45 @@ description: | 5. Contact the owner of the originating account to verify if the activity was legitimate 6. If unauthorized, immediately revoke the assumed role session and review all IAM trust policies where: | - equals("log.eventSource", "sts.amazonaws.com") && - equals("log.eventName", "AssumeRole") && - exists("log.userIdentityAccountId") && - exists("log.responseElementsAssumedRoleUserArn") && - exists("log.sourceIPAddress") && - equals("log.errorCode", "") && - !contains(safe(log.responseElementsAssumedRoleUserArn, ""), safe(log.userIdentityAccountId, "")) + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","sts.amazonaws.com") && + oneOf("log.eventName",["AssumeRole"]) && + equals("actionResult","success") && + regexMatch("log.userIdentity.accountId","^[0-9]{12}$") && + regexMatch("log.responseElements.assumedRoleUser.arn","^arn:[^:]+:sts::[0-9]{12}:assumed-role/.+$") && + !regexMatch("log.responseElements.assumedRoleUser.arn","^arn:[^:]+:sts::" + safe("log.userIdentity.accountId", "") + ":assumed-role/"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - - field: log.eventName - operator: filter_term - value: 'AssumeRole' - within: 15m - count: 15 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.cross_account_access_anomalies + operator: filter_term + value: match + within: 15m + count: 15 groupBy: - - lastEvent.log.responseElementsAssumedRoleUserArn - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentityAccountId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.responseElementsAssumedRoleUserArn +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentityAccountId diff --git a/rules/cloud/aws/aws/ebs_snapshot_sharing_violations.yml b/rules/cloud/aws/aws/ebs_snapshot_sharing_violations.yml index d2bb43dae..76f8b425c 100644 --- a/rules/cloud/aws/aws/ebs_snapshot_sharing_violations.yml +++ b/rules/cloud/aws/aws/ebs_snapshot_sharing_violations.yml @@ -1,38 +1,33 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: EBS Snapshot Sharing Violations +- aws +name: AWS EBS Snapshot Sharing Change for Review impact: confidentiality: 3 integrity: 2 availability: 1 category: Exfiltration -technique: "T1537 - Transfer Data to Cloud Account" +technique: T1537 - Transfer Data to Cloud Account adversary: origin references: - - https://securitylabs.datadoghq.com/cloud-security-atlas/attacks/sharing-ebs-snapshot/ - - https://attack.mitre.org/techniques/T1537/ +- https://securitylabs.datadoghq.com/cloud-security-atlas/attacks/sharing-ebs-snapshot/ +- https://attack.mitre.org/techniques/T1537/ description: | - Detects potential data exfiltration through EBS snapshot sharing. Monitors for CreateSnapshot followed by ModifySnapshotAttribute events that could indicate an attacker creating snapshots and sharing them with external accounts or making them public. - - Next Steps: - 1. Identify the affected snapshot ID and volume ID from the event details - 2. Review the account or group the snapshot was shared with in requestParameters - 3. Verify if the sharing was authorized by checking with the account owner - 4. If unauthorized, immediately revoke the snapshot sharing permissions - 5. Review CloudTrail logs for other suspicious activities by the same user/role - 6. Check if the source volume contains sensitive data - 7. Consider implementing preventive controls using IAM policies or SCPs to restrict snapshot sharing + Detects a successful addition of EBS snapshot volume-creation permissions. Check whether the destination accounts or public group are authorized. Removing permissions does not match, and a sharing change alone does not establish a policy violation. where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "ModifySnapshotAttribute") && - equals("log.errorCode", "") && - ( - contains("log.requestParameters", "CREATE_VOLUME_PERMISSION") || - contains("log.requestParameters", "createVolumePermission") || - (contains("log.requestParameters", "group") && contains("log.requestParameters", "all")) - ) + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ec2.amazonaws.com") && + oneOf("log.eventName",["ModifySnapshotAttribute"]) && + equals("actionResult","success") && + (greaterThan("log.requestParameters.createVolumePermission.add.items.#",0) || + greaterThan("log.requestParameters.createVolumePermission.add.#",0) || + (equals("log.requestParameters.attribute","createVolumePermission") && + equals("log.requestParameters.operationType","add") && + (greaterThan("log.requestParameters.userIds.#",0) || + greaterThan("log.requestParameters.userGroups.#",0))))) groupBy: - - lastEvent.log.responseElements.snapshotId - - lastEvent.log.userIdentity.accountId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.requestParameters.snapshotId +- lastEvent.log.userIdentity.accountId diff --git a/rules/cloud/aws/aws/ec2_cryptomining_detection.yml b/rules/cloud/aws/aws/ec2_cryptomining_detection.yml index 1ade0b26a..afbd8fed7 100644 --- a/rules/cloud/aws/aws/ec2_cryptomining_detection.yml +++ b/rules/cloud/aws/aws/ec2_cryptomining_detection.yml @@ -1,35 +1,28 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS EC2 Cryptomining Instance Launch Detection +- aws +name: AWS Compute-Intensive Instance Launch Request impact: confidentiality: 1 integrity: 2 availability: 3 category: Resource Hijacking -technique: "T1496 - Resource Hijacking" +technique: T1496 - Resource Hijacking adversary: origin references: - - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html - - https://attack.mitre.org/techniques/T1496/ +- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html +- https://attack.mitre.org/techniques/T1496/ description: | - Detects EC2 instance launches using GPU or high-compute instance types commonly associated with cryptomining operations. Attackers with compromised AWS credentials frequently launch expensive GPU instances (p3, p4, g4, g5) or large compute-optimized instances for cryptocurrency mining. - - Next Steps: - 1. Verify the identity launching the instance ({{log.userIdentityArn}}) and confirm authorization - 2. Check if the instance type matches legitimate workloads for the account - 3. Review the source IP ({{log.sourceIPAddress}}) for suspicious origins - 4. Examine the AMI used for the instance launch for known mining software - 5. Check billing dashboards for unexpected cost spikes - 6. If unauthorized, terminate the instance immediately and rotate compromised credentials - 7. Review IAM policies to restrict instance type launches using SCPs - 8. Enable AWS Budgets alerts for cost anomaly detection + Detects a successful RunInstances request for the listed GPU or large compute instance types. These types have legitimate uses; verify authorization, requested capacity and workload. The instance type does not establish cryptomining. where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "RunInstances") && - equals("log.errorCode", "") && - regexMatch("log.requestParameters.instanceType", "(?i)^(p3|p4d|p4de|p5|g4dn|g4ad|g5|g5g|c5\\.18xlarge|c5\\.24xlarge|c5a\\.24xlarge|c6i\\.32xlarge|c7g\\.16xlarge)") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ec2.amazonaws.com") && + oneOf("log.eventName",["RunInstances"]) && + equals("actionResult","success") && + regexMatch("log.requestParameters.instanceType", "^(p3|p4d|p4de|p5|g4dn|g4ad|g5|g5g)\\.[a-z0-9]+$|^(c5\\.(18|24)xlarge|c5a\\.24xlarge|c6i\\.32xlarge|c7g\\.16xlarge)$")) groupBy: - - adversary.user - - lastEvent.log.requestParameters.instanceType +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.instanceType diff --git a/rules/cloud/aws/aws/ec2_metadata_abuse.yml b/rules/cloud/aws/aws/ec2_metadata_abuse.yml index 3ca2edd0f..b23a2e8e8 100644 --- a/rules/cloud/aws/aws/ec2_metadata_abuse.yml +++ b/rules/cloud/aws/aws/ec2_metadata_abuse.yml @@ -1,36 +1,28 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: EC2 Instance Metadata Abuse +- aws +name: AWS EC2 Metadata Token Requirement Relaxed impact: confidentiality: 3 integrity: 2 availability: 1 category: Credential Access -technique: "T1552.005 - Unsecured Credentials: Cloud Instance Metadata API" +technique: 'T1552.005 - Unsecured Credentials: Cloud Instance Metadata API' adversary: origin references: - - https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ - - https://attack.mitre.org/techniques/T1552/005/ +- https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/ +- https://attack.mitre.org/techniques/T1552/005/ description: | - Detects potential abuse of EC2 instance metadata service (IMDS) which could indicate SSRF exploitation or credential theft. Monitors for unusual API calls using credentials with IMDSv1 role delivery or suspicious patterns of EC2 metadata access. - - Next Steps: - 1. Identify the EC2 instance and application making the metadata requests - 2. Check if the instance has IMDSv2 enforced (HttpTokens set to "required") - 3. Review the instance's IAM role permissions and recent API activity - 4. Investigate any web applications running on the instance for SSRF vulnerabilities - 5. Check CloudTrail logs for unusual API calls using the instance profile credentials - 6. If unauthorized access is confirmed, rotate the instance profile credentials and enforce IMDSv2 + Detects a successful ModifyInstanceMetadataOptions request setting httpTokens to optional. This permits use of IMDSv1; it does not prove that instance credentials were accessed. Verify the change and instance metadata configuration. where: | - (equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "ModifyInstanceMetadataOptions") && - equals("log.errorCode", "") && - contains("log.requestParameters", "httpTokens\":\"optional")) || - (exists("log.requestParameters") && - contains("log.requestParameters", "ec2:RoleDelivery\":\"1.0") && - equals("log.errorCode", "")) + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ec2.amazonaws.com") && + oneOf("log.eventName",["ModifyInstanceMetadataOptions"]) && + equals("actionResult","success") && + equals("log.requestParameters.httpTokens","optional")) groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentityAccountId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentityAccountId diff --git a/rules/cloud/aws/aws/ecs_eks_container_abuse.yml b/rules/cloud/aws/aws/ecs_eks_container_abuse.yml index be3586717..7fa86025a 100644 --- a/rules/cloud/aws/aws/ecs_eks_container_abuse.yml +++ b/rules/cloud/aws/aws/ecs_eks_container_abuse.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS ECS/EKS Container Abuse Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Execution -technique: "T1610 - Deploy Container" +technique: T1610 - Deploy Container adversary: origin references: - - https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security.html - - https://attack.mitre.org/techniques/T1610/ +- https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security.html +- https://attack.mitre.org/techniques/T1610/ description: | Detects suspicious container operations in AWS ECS and EKS including registration of task definitions with privileged mode, host network access, or sensitive volume mounts. Attackers may deploy malicious containers to execute code, escalate privileges, or access host resources. @@ -26,15 +25,19 @@ description: | 7. If unauthorized, deregister the task definition and terminate running tasks 8. Implement OPA/Gatekeeper policies to prevent privileged containers where: | - (equals("log.eventSource", "ecs.amazonaws.com") && - equals("log.eventName", "RegisterTaskDefinition") && - equals("log.errorCode", "") && - (contains("log.requestParameters", "\"privileged\":true") || - contains("log.requestParameters", "\"networkMode\":\"host\"") || - contains("log.requestParameters", "\"pidMode\":\"host\"") || - contains("log.requestParameters", "/var/run/docker.sock") || - contains("log.requestParameters", "/etc/shadow") || - contains("log.requestParameters", "/root"))) + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ecs.amazonaws.com") && + oneOf("log.eventName",["RegisterTaskDefinition"]) && + equals("actionResult","success") && + (exists('log.requestParameters.containerDefinitions.#(privileged==true)') || + equals("log.requestParameters.networkMode","host") || + equals("log.requestParameters.pidMode","host") || + exists('log.requestParameters.volumes.#(host.sourcePath=="/var/run/docker.sock")') || + exists('log.requestParameters.volumes.#(host.sourcePath=="/etc/shadow")') || + exists('log.requestParameters.volumes.#(host.sourcePath=="/root")'))) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/guardduty_high_severity_findings.yml b/rules/cloud/aws/aws/guardduty_high_severity_findings.yml index 37ee7c58e..4e5a4b703 100644 --- a/rules/cloud/aws/aws/guardduty_high_severity_findings.yml +++ b/rules/cloud/aws/aws/guardduty_high_severity_findings.yml @@ -1,31 +1,23 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS GuardDuty High-Severity Finding +- aws +name: AWS GuardDuty High or Critical Finding impact: confidentiality: 3 integrity: 3 availability: 2 -category: Defense Evasion, Persistence, Privilege Escalation, Initial Access -technique: "T1078 - Valid Accounts" +category: Threat Detection adversary: origin references: - - https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html - - https://attack.mitre.org/techniques/T1078/ +- https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html description: | - Detects high-severity findings from AWS GuardDuty indicating potential security threats such as malicious activity, unauthorized access, or compromised instances. GuardDuty analyzes CloudTrail events, VPC Flow Logs, and DNS logs to identify threats. - - Next Steps: - 1. Review the specific GuardDuty finding details including the threat type and affected resources - 2. Check the affected AWS account and region for any unauthorized changes - 3. Investigate the source IP addresses and user activities associated with the finding - 4. Review CloudTrail logs for suspicious API calls around the time of the finding - 5. If compromise is confirmed, rotate credentials and review IAM permissions - 6. Enable AWS CloudTrail logging if not already enabled - 7. Consider implementing AWS Security Hub for centralized security findings -where: equals("log.eventSource", "guardduty.amazonaws.com") && greaterOrEqual("log.severity", 7) + Detects an explicitly identified GuardDuty EventBridge finding with severity from 7 through 10. The CloudWatch log group must receive those EventBridge events; GuardDuty management API calls are not findings. Review the original finding detail for affected resources and threat context. +where: | + equals("log.awsRecordType","guardduty") && + greaterOrEqual("log.detail.severity",7) && + lessOrEqual("log.detail.severity",10) groupBy: - - lastEvent.log.accountId - - lastEvent.log.region - - lastEvent.log.type +- lastEvent.dataSource +- lastEvent.log.account +- lastEvent.log.region +- lastEvent.log.detail.id diff --git a/rules/cloud/aws/aws/iam_backdoor_creation_attempts.yml b/rules/cloud/aws/aws/iam_backdoor_creation_attempts.yml index fd644e9f1..4890e5da5 100644 --- a/rules/cloud/aws/aws/iam_backdoor_creation_attempts.yml +++ b/rules/cloud/aws/aws/iam_backdoor_creation_attempts.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS IAM Backdoor Creation Attempts impact: confidentiality: 3 integrity: 3 availability: 2 category: Privilege Escalation -technique: "T1136.003 - Create Account: Cloud Account" +technique: 'T1136.003 - Create Account: Cloud Account' adversary: origin references: - - https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage.html - - https://attack.mitre.org/techniques/T1136/003/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage.html +- https://attack.mitre.org/techniques/T1136/003/ description: | Detects potential IAM backdoor creation attempts through suspicious IAM user creation, access key generation, or policy attachment activities that could provide persistent access. - + Next Steps: 1. Review the source IP address and user identity performing these actions 2. Verify if the IAM user creation and policy attachments are authorized @@ -23,18 +22,42 @@ description: | 4. Review the policies attached to determine if they grant excessive permissions 5. If unauthorized, immediately disable the created user and revoke access keys 6. Investigate other activities from the same source IP or user identity -where: equals("log.eventSource", "iam.amazonaws.com") && oneOf("log.eventName", ["CreateUser", "CreateAccessKey", "AttachUserPolicy", "PutUserPolicy", "CreateLoginProfile"]) && equals("log.errorCode", "") +where: | + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "iam.amazonaws.com") && + oneOf("log.eventName", ["CreateUser", "CreateAccessKey", "AttachUserPolicy", "PutUserPolicy", "CreateLoginProfile"]) && + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - - field: log.eventSource - operator: filter_term - value: 'iam.amazonaws.com' - within: 30m - count: 3 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.iam_backdoor_creation_attempts + operator: filter_term + value: match + within: 30m + count: 3 groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentity.arn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentity.arn diff --git a/rules/cloud/aws/aws/iam_privilege_escalation_paths.yml b/rules/cloud/aws/aws/iam_privilege_escalation_paths.yml index 34d292ccc..49560ccc1 100644 --- a/rules/cloud/aws/aws/iam_privilege_escalation_paths.yml +++ b/rules/cloud/aws/aws/iam_privilege_escalation_paths.yml @@ -1,19 +1,18 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS IAM Privilege Escalation Path Detection impact: confidentiality: 3 integrity: 3 availability: 2 category: Privilege Escalation -technique: "T1098 - Account Manipulation" +technique: T1098 - Account Manipulation adversary: origin references: - - https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage.html - - https://attack.mitre.org/techniques/T1098/ - - https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage.html +- https://attack.mitre.org/techniques/T1098/ +- https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/ description: | Detects IAM actions commonly chained for privilege escalation including PassRole, CreatePolicyVersion, AttachUserPolicy, and AssumeRole. Attackers exploit these API calls to elevate their permissions within an AWS environment by creating new policy versions with elevated privileges or assuming roles with higher access. @@ -27,20 +26,46 @@ description: | 7. Implement permission boundaries to limit privilege escalation paths 8. Enable AWS CloudTrail Insights for anomaly detection on IAM operations where: | - equals("log.eventSource", "iam.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "iam.amazonaws.com") && oneOf("log.eventName", ["CreatePolicyVersion", "SetDefaultPolicyVersion", "AttachUserPolicy", "AttachGroupPolicy", "AttachRolePolicy", "PutUserPolicy", "PutGroupPolicy", "PutRolePolicy", "AddUserToGroup"]) && - equals("log.errorCode", "") + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventSource - operator: filter_term - value: iam.amazonaws.com - within: 30m - count: 3 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.iam_privilege_escalation_paths + operator: filter_term + value: match + within: 30m + count: 3 groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/lambda_privilege_escalation.yml b/rules/cloud/aws/aws/lambda_privilege_escalation.yml index d97e5ad77..cafb93741 100644 --- a/rules/cloud/aws/aws/lambda_privilege_escalation.yml +++ b/rules/cloud/aws/aws/lambda_privilege_escalation.yml @@ -1,46 +1,61 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: Lambda Function Privilege Escalation +- aws +name: Repeated AWS Administrative Policy Attachments impact: confidentiality: 3 integrity: 3 availability: 2 category: Privilege Escalation -technique: "T1548 - Abuse Elevation Control Mechanism" adversary: origin references: - - https://bishopfox.com/blog/privilege-escalation-in-aws - - https://attack.mitre.org/techniques/T1548/ +- https://bishopfox.com/blog/privilege-escalation-in-aws +- https://attack.mitre.org/techniques/T1098/ description: | - Detects potential privilege escalation through Lambda functions when IAM policies are attached to roles or users that can be exploited. This may indicate an attacker attempting to escalate privileges by attaching administrative policies to Lambda execution roles. - - Next Steps: - 1. Verify if the policy attachment was authorized and follows change management procedures - 2. Review the attached policy permissions, especially if AdministratorAccess or IAMFullAccess policies were attached - 3. Check the Lambda function's code and recent invocations for suspicious activity - 4. Review CloudTrail logs for other IAM changes by the same user/role - 5. Validate if the Lambda function legitimately requires the elevated permissions - 6. Consider revoking the policy attachment if unauthorized and investigate the source of the change - 7. Check for any unusual Lambda function executions following the policy attachment - 8. Review the user/role history for previous privilege escalation attempts + Detects repeated successful attachment of the AWS-managed AdministratorAccess or IAMFullAccess policy to a role or user by the same actor and account within one hour. Review whether these grants were approved. The request alone does not identify a Lambda execution role. where: | - equals("log.eventSource", "iam.amazonaws.com") && - oneOf("log.eventName", ["AttachRolePolicy", "AttachUserPolicy"]) && - equals("log.errorCode", "") && - (contains("log.requestParameters.policyArn", "AdministratorAccess") || contains("log.requestParameters.policyArn", "IAMFullAccess")) + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","iam.amazonaws.com") && + oneOf("log.eventName",["AttachRolePolicy", "AttachUserPolicy"]) && + equals("actionResult","success") && + regexMatch("log.requestParameters.policyArn","^arn:(aws|aws-cn|aws-us-gov):iam::aws:policy/(AdministratorAccess|IAMFullAccess)$"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentity.arn - operator: filter_term - value: '{{.log.userIdentity.arn}}' - - field: log.eventName - operator: filter_term - value: AttachRolePolicy - within: 1h - count: 2 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.lambda_privilege_escalation + operator: filter_term + value: match + within: 1h + count: 2 groupBy: - - lastEvent.log.requestParameters.roleArn - - lastEvent.log.userIdentity.arn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.requestParameters.roleArn +- lastEvent.log.userIdentity.arn +technique: T1098 - Account Manipulation diff --git a/rules/cloud/aws/aws/lambda_url_backdoor.yml b/rules/cloud/aws/aws/lambda_url_backdoor.yml index 28487ea6c..c04290f2e 100644 --- a/rules/cloud/aws/aws/lambda_url_backdoor.yml +++ b/rules/cloud/aws/aws/lambda_url_backdoor.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Lambda Function URL Backdoor Creation impact: confidentiality: 3 integrity: 3 availability: 1 category: Persistence -technique: "T1059 - Command and Scripting Interpreter" +technique: T1059 - Command and Scripting Interpreter adversary: origin references: - - https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html - - https://attack.mitre.org/techniques/T1059/ +- https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html +- https://attack.mitre.org/techniques/T1059/ description: | Detects creation of Lambda function URLs which provide direct HTTPS endpoints to Lambda functions. Attackers may create function URL configurations as backdoor endpoints for persistent access, command execution, or data exfiltration without requiring API Gateway. @@ -26,10 +25,14 @@ description: | 7. Review CloudTrail logs for the function's invocation history 8. Implement SCPs to restrict Lambda function URL creation to authorized roles where: | - equals("log.eventSource", "lambda.amazonaws.com") && - oneOf("log.eventName", ["CreateFunctionUrlConfig", "UpdateFunctionUrlConfig"]) && - equals("log.errorCode", "") && - contains("log.requestParameters", "\"authType\":\"NONE\"") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","lambda.amazonaws.com") && + oneOf("log.eventName",["CreateFunctionUrlConfig", "UpdateFunctionUrlConfig"]) && + equals("actionResult","success") && + equals("log.requestParameters.authType","NONE")) groupBy: - - adversary.user - - lastEvent.log.requestParameters.functionName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.functionName diff --git a/rules/cloud/aws/aws/mass_resource_deletion.yml b/rules/cloud/aws/aws/mass_resource_deletion.yml index 921fdb70e..8d9cdbd4a 100644 --- a/rules/cloud/aws/aws/mass_resource_deletion.yml +++ b/rules/cloud/aws/aws/mass_resource_deletion.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Mass Resource Deletion impact: confidentiality: 1 integrity: 3 availability: 3 category: Impact -technique: "T1485 - Data Destruction" +technique: T1485 - Data Destruction adversary: origin references: - - https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html - - https://attack.mitre.org/techniques/T1485/ +- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html +- https://attack.mitre.org/techniques/T1485/ description: | Detects mass deletion of AWS resources which could indicate destructive attack or insider threat. Monitors for multiple delete operations across various AWS services within a 10-minute window. - + Next Steps: 1. Immediately identify the user/role performing the deletions through the userIdentity.arn field 2. Review CloudTrail logs to determine the scope and specific resources being deleted @@ -25,15 +24,47 @@ description: | 6. Consider implementing SCPs (Service Control Policies) to prevent mass deletions 7. Review and restore deleted resources from backups if necessary 8. Document the incident and update IAM policies to prevent future occurrences -where: (contains("log.eventName", "Delete") || contains("log.eventName", "Terminate") || contains("log.eventName", "Remove")) && equals("log.errorCode", "") && !equals("log.eventSource", "s3.amazonaws.com") +where: | + (equals("log.awsRecordType","cloudtrail") && + (regexMatch("log.eventName","^(Delete|Terminate|Remove)[A-Z]") && + equals("actionResult","success") && + !equals("log.eventSource","s3.amazonaws.com"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentity.arn - operator: filter_term - value: '{{.log.userIdentity.arn}}' - within: 10m - count: 15 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.mass_resource_deletion + operator: filter_term + value: match + within: 10m + count: 15 groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentity.arn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentity.arn diff --git a/rules/cloud/aws/aws/rds_security_group_changes.yml b/rules/cloud/aws/aws/rds_security_group_changes.yml index 6f3d579e3..84e7c837d 100644 --- a/rules/cloud/aws/aws/rds_security_group_changes.yml +++ b/rules/cloud/aws/aws/rds_security_group_changes.yml @@ -1,34 +1,29 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: RDS Security Group Changes +- aws +name: AWS RDS Security Group Internet Ingress Added impact: confidentiality: 3 integrity: 2 availability: 2 category: Defense Evasion -technique: "T1562.007 - Impair Defenses: Disable or Modify Cloud Firewall" +technique: 'T1562.007 - Impair Defenses: Disable or Modify Cloud Firewall' adversary: origin references: - - https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/logging-using-cloudtrail.html - - https://attack.mitre.org/techniques/T1562/007/ +- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/logging-using-cloudtrail.html +- https://attack.mitre.org/techniques/T1562/007/ description: | - Detects modifications to RDS database security groups that could expose databases to unauthorized access. This includes adding new ingress rules or modifying existing security group configurations that may compromise database security. - - Next Steps: - 1. Review the security group changes to determine if they were authorized - 2. Check if the changes expose RDS instances to public internet (0.0.0.0/0) - 3. Verify the identity of the user who made the changes and confirm authorization - 4. Review CloudTrail logs for additional suspicious activities by the same user - 5. If unauthorized, immediately revert the security group changes - 6. Consider implementing preventive controls using AWS Config rules or SCPs - 7. Document the incident and update change management procedures if needed + Detects a successful AuthorizeDBSecurityGroupIngress request allowing 0.0.0.0/0. Revoking a permission does not add exposure. Review the database security group and business justification. where: | - equals("log.eventSource", "rds.amazonaws.com") && - oneOf("log.eventName", ["AuthorizeDBSecurityGroupIngress", "CreateDBSecurityGroup", "DeleteDBSecurityGroup", "RevokeDBSecurityGroupIngress"]) && - equals("log.errorCode", "") && - contains("log.requestParameters", "0.0.0.0/0") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","rds.amazonaws.com") && + oneOf("log.eventName",["AuthorizeDBSecurityGroupIngress"]) && + equals("actionResult","success") && + (equals("log.requestParameters.cidrip","0.0.0.0/0") || + equals("log.requestParameters.cidrIp","0.0.0.0/0"))) groupBy: - - lastEvent.log.eventName - - lastEvent.log.userIdentityArn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.eventName +- lastEvent.log.userIdentityArn diff --git a/rules/cloud/aws/aws/rds_snapshot_exfiltration.yml b/rules/cloud/aws/aws/rds_snapshot_exfiltration.yml index 4afc9dfe4..6d6c988db 100644 --- a/rules/cloud/aws/aws/rds_snapshot_exfiltration.yml +++ b/rules/cloud/aws/aws/rds_snapshot_exfiltration.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS RDS Snapshot Sharing for Data Exfiltration impact: confidentiality: 3 integrity: 2 availability: 1 category: Data Exfiltration -technique: "T1537 - Transfer Data to Cloud Account" +technique: T1537 - Transfer Data to Cloud Account adversary: origin references: - - https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ShareSnapshot.html - - https://attack.mitre.org/techniques/T1537/ +- https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ShareSnapshot.html +- https://attack.mitre.org/techniques/T1537/ description: | Detects RDS snapshot sharing modifications that could be used for data exfiltration. Attackers may share database snapshots with external AWS accounts to exfiltrate sensitive data, or make snapshots public to download them from an attacker-controlled account. @@ -26,11 +25,16 @@ description: | 7. Check for other snapshot operations by the same principal 8. Implement preventive SCPs to restrict snapshot sharing where: | - equals("log.eventSource", "rds.amazonaws.com") && - oneOf("log.eventName", ["ModifyDBSnapshotAttribute", "ModifyDBClusterSnapshotAttribute"]) && - equals("log.errorCode", "") && - (contains("log.requestParameters", "attributeName\":\"restore") || - contains("log.requestParameters", "all")) + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","rds.amazonaws.com") && + oneOf("log.eventName",["ModifyDBSnapshotAttribute", "ModifyDBClusterSnapshotAttribute"]) && + equals("actionResult","success") && + equals("log.requestParameters.attributeName","restore") && + (greaterThan("log.requestParameters.valuesToAdd.#",0) || + greaterThan("log.requestParameters.valuesToAdd.items.#",0))) groupBy: - - adversary.user - - lastEvent.log.requestParameters.dBSnapshotIdentifier +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.dBSnapshotIdentifier diff --git a/rules/cloud/aws/aws/root_account_usage_without_mfa.yml b/rules/cloud/aws/aws/root_account_usage_without_mfa.yml index 6933387e0..e78423c93 100644 --- a/rules/cloud/aws/aws/root_account_usage_without_mfa.yml +++ b/rules/cloud/aws/aws/root_account_usage_without_mfa.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Root Account Usage Without MFA impact: confidentiality: 3 integrity: 3 availability: 2 category: Defense Evasion, Persistence, Privilege Escalation, Initial Access -technique: "T1078.004 - Valid Accounts: Cloud Accounts" +technique: 'T1078.004 - Valid Accounts: Cloud Accounts' adversary: origin references: - - https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html - - https://attack.mitre.org/techniques/T1078/004/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html +- https://attack.mitre.org/techniques/T1078/004/ description: | Detects usage of AWS root account without Multi-Factor Authentication (MFA). Root account usage should be avoided for daily operations and must always use MFA when necessary. This is a critical security violation as the root account has unrestricted access to all AWS resources. - + Next Steps: 1. Immediately verify if this root account activity is authorized 2. Check the source IP address and user agent for suspicious patterns @@ -28,9 +27,15 @@ description: | 9. Check for any privilege escalation attempts or unusual API calls 10. Implement additional monitoring for root account activities where: | - equals("log.userIdentityType", "Root") && - !equals("log.userIdentitySessionContextAttributesMfaAuthenticated", "true") && - equals("log.errorCode", "") + equals("log.awsRecordType","cloudtrail") && + (equals("log.userIdentity.type","Root") && + equals("actionResult","success") && + (equals("log.userIdentity.sessionContext.attributes.mfaAuthenticated","false") || + (equals("log.eventName","ConsoleLogin") && + equals("log.additionalEventData.MFAUsed","No")))) groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentityAccountId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentityAccountId diff --git a/rules/cloud/aws/aws/route53_dns_hijacking.yml b/rules/cloud/aws/aws/route53_dns_hijacking.yml index 722103fab..7557e92a4 100644 --- a/rules/cloud/aws/aws/route53_dns_hijacking.yml +++ b/rules/cloud/aws/aws/route53_dns_hijacking.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Route 53 DNS Hijacking Attempt impact: confidentiality: 3 integrity: 3 availability: 3 category: Initial Access -technique: "T1584.002 - Compromise Infrastructure: DNS Server" +technique: 'T1584.002 - Compromise Infrastructure: DNS Server' adversary: origin references: - - https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/logging-using-cloudtrail.html - - https://attack.mitre.org/techniques/T1584/002/ +- https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/logging-using-cloudtrail.html +- https://attack.mitre.org/techniques/T1584/002/ description: | Detects potential DNS hijacking attempts through unauthorized changes to Route 53 DNS records. This rule monitors for ChangeResourceRecordSets operations that could indicate an attacker modifying DNS entries to redirect traffic. - + Next Steps: 1. Review the CloudTrail logs to identify what DNS records were modified and the specific changes made 2. Verify if the user identity making the changes is authorized to modify Route 53 records @@ -25,18 +24,47 @@ description: | 6. Enable MFA for all users with Route 53 permissions 7. Consider implementing AWS Config rules to monitor Route 53 changes 8. Review and restrict IAM policies for Route 53 access -where: equals("log.eventSource", "route53.amazonaws.com") && equals("log.eventName", "ChangeResourceRecordSets") && equals("log.errorCode", "") +where: | + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "route53.amazonaws.com") && + equals("log.eventName", "ChangeResourceRecordSets") && + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventName - operator: filter_term - value: 'ChangeResourceRecordSets' - within: 30m - count: 10 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.route53_dns_hijacking + operator: filter_term + value: match + within: 30m + count: 10 groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentityArn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentityArn diff --git a/rules/cloud/aws/aws/s3_bucket_public_exposure.yml b/rules/cloud/aws/aws/s3_bucket_public_exposure.yml index bacbb1cb1..9e033c562 100644 --- a/rules/cloud/aws/aws/s3_bucket_public_exposure.yml +++ b/rules/cloud/aws/aws/s3_bucket_public_exposure.yml @@ -1,40 +1,37 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS S3 Bucket Public Exposure +- aws +name: AWS S3 Public Access Controls Relaxed impact: confidentiality: 3 integrity: 2 availability: 1 category: Collection -technique: "T1530 - Data from Cloud Storage Object" +technique: T1530 - Data from Cloud Storage Object adversary: origin references: - - https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html - - https://attack.mitre.org/techniques/T1530/ +- https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html +- https://attack.mitre.org/techniques/T1530/ description: | - Detects S3 bucket configuration changes that expose buckets to public access, including ACL modifications and public access block removal. This can lead to unauthorized data exposure and potential data breach. - - Next Steps: - 1. Immediately review the affected S3 bucket permissions and revert unauthorized changes - 2. Check CloudTrail logs for the source of the configuration change - 3. Verify if the change was authorized by examining change management tickets - 4. Review bucket contents to determine sensitivity of potentially exposed data - 5. Enable S3 Block Public Access at the account level to prevent future exposures - 6. Implement bucket policies that restrict public access - 7. Enable S3 access logging for the affected bucket - 8. Consider implementing AWS Config rules to monitor S3 bucket permissions + Detects a successful public ACL change or removal/relaxation of bucket Public Access Block controls. Account-level and bucket policies can still prevent public access. Review the effective controls before concluding that data is exposed. where: | - equals("log.eventSource", "s3.amazonaws.com") && - (equals("log.eventName", "PutBucketAcl") || - equals("log.eventName", "PutBucketPublicAccessBlock") || - equals("log.eventName", "DeleteBucketPublicAccessBlock") || - equals("log.eventName", "PutObjectAcl")) && - equals("log.errorCode", "") && - (oneOf("log.requestParameters.x-amz-acl", ["public-read", "public-read-write"]) || - contains("log.requestParameters.acl", "AllUsers") || - equals("log.eventName", "DeleteBucketPublicAccessBlock")) + equals("log.awsRecordType","cloudtrail") && + (equals("actionResult","success") && + (equals("log.eventSource","s3.amazonaws.com") && + oneOf("log.eventName",["PutBucketAcl", "PutObjectAcl"]) && + (oneOf("log.requestParameters.xamzacl",["public-read", "public-read-write"]) || + exists('log.requestParameters.AccessControlPolicy.AccessControlList.Grant.#(Grantee.URI=="http://acs.amazonaws.com/groups/global/AllUsers")')) || + equals("log.eventSource","s3.amazonaws.com") && + oneOf("log.eventName",["DeleteBucketPublicAccessBlock"]) || + (equals("log.eventSource","s3.amazonaws.com") && + oneOf("log.eventName",["PutBucketPublicAccessBlock"]) && + (equals("log.requestParameters.PublicAccessBlockConfiguration.BlockPublicAcls",false) || + equals("log.requestParameters.PublicAccessBlockConfiguration.IgnorePublicAcls",false) || + equals("log.requestParameters.PublicAccessBlockConfiguration.BlockPublicPolicy",false) || + equals("log.requestParameters.PublicAccessBlockConfiguration.RestrictPublicBuckets",false))))) groupBy: - - lastEvent.log.requestParameters.bucketName - - lastEvent.log.userIdentity.accessKeyId +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.requestParameters.bucketName +- lastEvent.log.userIdentity.accessKeyId diff --git a/rules/cloud/aws/aws/s3_bulk_data_exfiltration.yml b/rules/cloud/aws/aws/s3_bulk_data_exfiltration.yml index 062b26f92..1f07c808a 100644 --- a/rules/cloud/aws/aws/s3_bulk_data_exfiltration.yml +++ b/rules/cloud/aws/aws/s3_bulk_data_exfiltration.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS S3 Bulk Data Exfiltration Detected impact: confidentiality: 2 integrity: 1 availability: 1 category: Data Exfiltration -technique: "T1530 - Data from Cloud Storage Object" +technique: T1530 - Data from Cloud Storage Object adversary: origin references: - - https://docs.aws.amazon.com/AmazonS3/latest/userguide/cloudtrail-logging.html - - https://attack.mitre.org/techniques/T1530/ +- https://docs.aws.amazon.com/AmazonS3/latest/userguide/cloudtrail-logging.html +- https://attack.mitre.org/techniques/T1530/ description: | Detects bulk GetObject operations from S3 buckets indicating potential data exfiltration. When an attacker gains access to AWS credentials, they may attempt to download large amounts of data from S3 buckets in a short period. @@ -26,20 +25,46 @@ description: | 7. Enable S3 server access logging for affected buckets 8. Review S3 bucket policies and tighten access controls where: | - equals("log.eventSource", "s3.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "s3.amazonaws.com") && equals("log.eventName", "GetObject") && - equals("log.errorCode", "") + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventName - operator: filter_term - value: GetObject - within: 15m - count: 100 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.s3_bulk_data_exfiltration + operator: filter_term + value: match + within: 15m + count: 100 groupBy: - - adversary.user - - lastEvent.log.requestParameters.bucketName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.requestParameters.bucketName diff --git a/rules/cloud/aws/aws/secrets_manager_suspicious_access.yml b/rules/cloud/aws/aws/secrets_manager_suspicious_access.yml index 68c7d3da8..786dd33be 100644 --- a/rules/cloud/aws/aws/secrets_manager_suspicious_access.yml +++ b/rules/cloud/aws/aws/secrets_manager_suspicious_access.yml @@ -1,21 +1,19 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Secrets Manager Suspicious Access Pattern impact: confidentiality: 3 integrity: 2 availability: 1 category: Credential Access -technique: "T1552.004 - Private Keys" adversary: origin references: - - https://docs.aws.amazon.com/secretsmanager/latest/userguide/monitoring-cloudtrail.html - - https://attack.mitre.org/techniques/T1552/004/ +- https://docs.aws.amazon.com/secretsmanager/latest/userguide/monitoring-cloudtrail.html +- https://attack.mitre.org/techniques/T1555/006/ description: | Detects unusual access patterns to AWS Secrets Manager that could indicate credential theft or unauthorized access attempts. This rule monitors for multiple GetSecretValue or BatchGetSecretValue operations from the same source within a short time window, which may indicate an attacker attempting to harvest credentials. - + Next Steps: 1. Review the CloudTrail logs for the identified user/role to understand which secrets were accessed 2. Check if the accessing identity has legitimate business need for these secrets @@ -23,28 +21,78 @@ description: | 4. Review the source IP addresses and locations for anomalies 5. If unauthorized, immediately rotate the accessed secrets and review IAM permissions 6. Check for any subsequent API calls using potentially compromised credentials -where: equals("log.eventSource", "secretsmanager.amazonaws.com") && (equals("log.eventName", "GetSecretValue") || equals("log.eventName", "BatchGetSecretValue")) && equals("log.errorCode", "") +where: | + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "secretsmanager.amazonaws.com") && + (equals("log.eventName", "GetSecretValue") || + equals("log.eventName", "BatchGetSecretValue")) && + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.secrets_manager_suspicious_access + operator: filter_term + value: match + - field: log.eventName + operator: filter_term + value: GetSecretValue + within: 10m + count: 10 + or: - indexPattern: v11-log-aws-* with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventName - operator: filter_term - value: GetSecretValue - within: 10m - count: 10 - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventName - operator: filter_term - value: BatchGetSecretValue + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.secrets_manager_suspicious_access + operator: filter_term + value: match + - field: log.eventName + operator: filter_term + value: BatchGetSecretValue within: 10m count: 5 groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentityArn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentityArn +technique: 'T1555.006 - Credentials from Password Stores: Cloud Secrets Management Stores' diff --git a/rules/cloud/aws/aws/security_group_modifications.yml b/rules/cloud/aws/aws/security_group_modifications.yml index 3ddb4b0af..61a5f8cad 100644 --- a/rules/cloud/aws/aws/security_group_modifications.yml +++ b/rules/cloud/aws/aws/security_group_modifications.yml @@ -1,21 +1,20 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Security Group Modifications impact: confidentiality: 2 integrity: 3 availability: 2 category: Defense Evasion -technique: "T1562.007 - Impair Defenses: Disable or Modify Cloud Firewall" +technique: 'T1562.007 - Impair Defenses: Disable or Modify Cloud Firewall' adversary: origin references: - - https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html - - https://attack.mitre.org/techniques/T1562/007/ +- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html +- https://attack.mitre.org/techniques/T1562/007/ description: | Detects modifications to AWS security groups that could weaken network security posture. Monitors for changes that add permissive rules or remove restrictive rules, particularly those allowing unrestricted access (0.0.0.0/0 or ::/0). - + Next Steps: 1. Review the security group change details in CloudTrail logs 2. Verify if the change was authorized and follows security policies @@ -25,28 +24,45 @@ description: | 6. Review other recent activities from the same source IP or user 7. Consider implementing preventive controls via AWS Config rules or SCPs where: | - equals("log.eventSource", "ec2.amazonaws.com") && - oneOf("log.eventName", ["AuthorizeSecurityGroupIngress", "AuthorizeSecurityGroupEgress", "RevokeSecurityGroupIngress", "RevokeSecurityGroupEgress", "CreateSecurityGroup", "DeleteSecurityGroup", "ModifySecurityGroupRules"]) && - exists("log.sourceIPAddress") && - equals("log.errorCode", "") && - ( - contains("log.requestParameters.ipPermissions.ipProtocol", "-1") || - contains("log.requestParameters.ipPermissions.cidrIp", "0.0.0.0/0") || - contains("log.requestParameters.ipPermissions.ipv6CidrIp", "::/0") || - contains("log.requestParameters.ipRanges.cidrIp", "0.0.0.0/0") || - contains("log.requestParameters.ipv6Ranges.cidrIpv6", "::/0") - ) + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ec2.amazonaws.com") && + oneOf("log.eventName",["AuthorizeSecurityGroupIngress", "AuthorizeSecurityGroupEgress", "ModifySecurityGroupRules"]) && + equals("actionResult","success") && + (exists('log.requestParameters.ipPermissions.items.#(ipRanges.items.#(cidrIp=="0.0.0.0/0"))') || + exists('log.requestParameters.ipPermissions.items.#(ipv6Ranges.items.#(cidrIpv6=="::/0"))') || + exists('log.requestParameters.securityGroupRuleSet.items.#(securityGroupRule.cidrIpv4=="0.0.0.0/0")') || + exists('log.requestParameters.securityGroupRuleSet.items.#(securityGroupRule.cidrIpv6=="::/0")')))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - - field: log.eventSource - operator: filter_term - value: 'ec2.amazonaws.com' - within: 30m - count: 3 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.security_group_modifications + operator: filter_term + value: match + within: 30m + count: 3 groupBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentity.arn +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentity.arn diff --git a/rules/cloud/aws/aws/ssm_session_abuse.yml b/rules/cloud/aws/aws/ssm_session_abuse.yml index 9a2335888..edcdb813c 100644 --- a/rules/cloud/aws/aws/ssm_session_abuse.yml +++ b/rules/cloud/aws/aws/ssm_session_abuse.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Systems Manager Session Abuse impact: confidentiality: 3 integrity: 3 availability: 2 category: Lateral Movement -technique: "T1021 - Remote Services" +technique: T1021 - Remote Services adversary: origin references: - - https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html - - https://attack.mitre.org/techniques/T1021/ +- https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html +- https://attack.mitre.org/techniques/T1021/ description: | Detects suspicious use of AWS Systems Manager (SSM) for remote access including StartSession, SendCommand, and SendSSHPublicKey. Attackers use SSM to establish interactive sessions or execute commands on EC2 instances without requiring direct SSH access or security group changes. @@ -26,20 +25,46 @@ description: | 7. Implement SSM session logging to S3 and CloudWatch for audit trails 8. Restrict SSM access using IAM policies with condition keys where: | - equals("log.eventSource", "ssm.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ssm.amazonaws.com") && oneOf("log.eventName", ["StartSession", "ResumeSession", "SendCommand", "StartAutomationExecution"]) && - equals("log.errorCode", "") + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.userIdentityArn - operator: filter_term - value: '{{.log.userIdentityArn}}' - - field: log.eventSource - operator: filter_term - value: ssm.amazonaws.com - within: 30m - count: 5 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.ssm_session_abuse + operator: filter_term + value: match + within: 30m + count: 5 groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/aws/sts_token_abuse.yml b/rules/cloud/aws/aws/sts_token_abuse.yml index f92b5b183..caa548d42 100644 --- a/rules/cloud/aws/aws/sts_token_abuse.yml +++ b/rules/cloud/aws/aws/sts_token_abuse.yml @@ -1,22 +1,21 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS STS Token Abuse Detection impact: confidentiality: 3 integrity: 3 availability: 1 category: Privilege Escalation -technique: "T1078.004 - Cloud Accounts" +technique: T1078.004 - Cloud Accounts adversary: origin references: - - https://docs.aws.amazon.com/IAM/latest/UserGuide/cloudtrail-integration.html - - https://attack.mitre.org/techniques/T1078/004/ - - https://www.elastic.co/security-labs/exploring-aws-sts-assumeroot +- https://docs.aws.amazon.com/IAM/latest/UserGuide/cloudtrail-integration.html +- https://attack.mitre.org/techniques/T1078/004/ +- https://www.elastic.co/security-labs/exploring-aws-sts-assumeroot description: | Detects potential abuse of AWS STS AssumeRole operations. This rule identifies when roles are assumed from unusual IP addresses or when there are multiple role assumptions in a short time period, which could indicate lateral movement or privilege escalation. The rule specifically flags AssumeRole operations performed without MFA authentication. - + Next Steps: 1. Verify if the source IP address belongs to your organization's known IP ranges 2. Check if the assumed role is appropriate for the user or service that initiated the request @@ -26,21 +25,45 @@ description: | 6. Consider implementing MFA requirements for sensitive role assumptions 7. Review and potentially restrict the trust policy for the assumed role where: | - equals("log.eventSource", "sts.amazonaws.com") && + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "sts.amazonaws.com") && equals("log.eventName", "AssumeRole") && - equals("log.errorCode", "") && - !equals("log.userIdentitySessionContextAttributesMfaAuthenticated", "true") && - !equals("log.userIdentityType", "AWSService") && + equals("actionResult","success") && + equals("log.userIdentity.sessionContext.attributes.mfaAuthenticated", "false") && + !equals("log.userIdentity.type", "AWSService") && !contains("log.userAgent", "aws-sdk") && - !contains("log.userAgent", "Botocore") + !contains("log.userAgent", "Botocore"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - within: 15m - count: 20 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.sts_token_abuse + operator: filter_term + value: match + within: 15m + count: 20 groupBy: - - lastEvent.log.userIdentityArn - - adversary.ip +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.userIdentityArn +- adversary.ip diff --git a/rules/cloud/aws/aws/unusual_api_call_patterns.yml b/rules/cloud/aws/aws/unusual_api_call_patterns.yml index 69dc52ac7..faa7c5582 100644 --- a/rules/cloud/aws/aws/unusual_api_call_patterns.yml +++ b/rules/cloud/aws/aws/unusual_api_call_patterns.yml @@ -1,21 +1,19 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS Unusual API Call Patterns impact: confidentiality: 2 integrity: 1 availability: 1 -category: Execution -technique: "T1106 - Execution through API" +category: Discovery adversary: origin references: - - https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html - - https://attack.mitre.org/techniques/T1106/ +- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html +- https://attack.mitre.org/techniques/T1580/ description: | Detects unusual API call patterns in AWS that may indicate unauthorized access or reconnaissance activities. This rule triggers when multiple sensitive API calls are made from the same source IP within a short time window, suggesting potential enumeration or discovery activities by attackers. - + Next Steps: - Review the source IP address and determine if it's authorized for AWS API access - Check if the user identity associated with these calls is legitimate and expected @@ -26,23 +24,46 @@ description: | - Consider blocking the source IP if unauthorized activity is confirmed - Implement additional monitoring for the affected account and resources where: | - exists("log.eventSource") && + (equals("log.awsRecordType","cloudtrail") && + (exists("log.eventSource") && + exists("log.sourceIPAddress") && exists("log.eventName") && ( oneOf("log.eventName", ["DescribeSecurityGroups", "DescribeNetworkAcls", "DescribeVpcs", "DescribeSubnets", "DescribeRouteTables", "DescribeInstances", "DescribeSnapshots", "DescribeVolumes", "DescribeImages", "DescribeKeyPairs", "ListBuckets", "GetBucketAcl", "GetBucketPolicy", "ListAccessKeys", "ListUsers", "ListRoles", "ListPolicies", "GetAccountAuthorizationDetails", "GenerateCredentialReport", "GetCredentialReport"]) ) && - equals("log.errorCode", "") + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - - field: log.eventName - operator: filter_match - value: 'Describe List Get Generate' - within: 10m - count: 50 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.unusual_api_call_patterns + operator: filter_term + value: match + within: 10m + count: 50 groupBy: - - lastEvent.log.sourceIPAddress +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +technique: T1580 - Cloud Infrastructure Discovery diff --git a/rules/cloud/aws/aws/vpc_flow_log_anomalies.yml b/rules/cloud/aws/aws/vpc_flow_log_anomalies.yml index 1749bec63..0d6f6bab9 100644 --- a/rules/cloud/aws/aws/vpc_flow_log_anomalies.yml +++ b/rules/cloud/aws/aws/vpc_flow_log_anomalies.yml @@ -1,51 +1,54 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: AWS VPC Flow Log Anomalies +- aws +name: Repeated AWS VPC Flow Log Deletion Requests impact: confidentiality: 3 integrity: 2 availability: 2 -category: Discovery -technique: "T1046 - Network Service Discovery" +category: Defense Evasion adversary: origin references: - - https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html - - https://attack.mitre.org/techniques/T1046/ +- https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html description: | - Detects anomalies in VPC Flow Logs configuration that could indicate attempts to hide malicious network activity. Monitors for deletion or modification of flow log configurations. - - Next Steps: - 1. Verify if the flow log changes were authorized by reviewing the AWS CloudTrail logs for the userIdentityArn - 2. Check if the source IP address belongs to known administrative systems or jump boxes - 3. Review other activities from the same source IP and user identity in the past 24-48 hours - 4. Examine the affected VPC and its resources to understand the impact of disabled flow logging - 5. If unauthorized, immediately re-enable flow logs and investigate what network activity may have occurred while logging was disabled - 6. Review IAM permissions for the user/role that made these changes to ensure least privilege - 7. Consider implementing preventive controls using AWS Config rules or SCPs to prevent unauthorized flow log modifications + Detects repeated successful DeleteFlowLogs API requests from the same source and AWS account within 24 hours. Review the flow-log identifiers and any per-resource failures in the response. API success alone does not prove that every requested log was deleted. where: | - equals("log.eventSource", "ec2.amazonaws.com") && - oneOf("log.eventName", ["DeleteFlowLogs", "CreateFlowLogs", "ModifyFlowLogsAttribute"]) && - exists("log.sourceIPAddress") && - equals("log.errorCode", "") && - ( - equals("log.eventName", "DeleteFlowLogs") || - (equals("log.eventName", "CreateFlowLogs") && contains("log.requestParameters.deliverLogsStatus", "FAILED")) || - (equals("log.eventName", "ModifyFlowLogsAttribute") && equals("log.requestParameters.deliverLogsStatus", "INACTIVE")) - ) + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","ec2.amazonaws.com") && + oneOf("log.eventName",["DeleteFlowLogs"]) && + equals("actionResult","success"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: log.sourceIPAddress - operator: filter_term - value: '{{.log.sourceIPAddress}}' - - field: log.eventName - operator: filter_term - value: 'DeleteFlowLogs' - within: 24h - count: 2 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.vpc_flow_log_anomalies + operator: filter_term + value: match + within: 24h + count: 2 deduplicateBy: - - lastEvent.log.sourceIPAddress - - lastEvent.log.userIdentity.arn - - lastEvent.log.awsRegion +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- lastEvent.log.sourceIPAddress +- lastEvent.log.userIdentity.arn +- lastEvent.log.awsRegion diff --git a/rules/cloud/aws/aws/waf_shield_rule_modifications.yml b/rules/cloud/aws/aws/waf_shield_rule_modifications.yml index f72514d75..baa844afd 100644 --- a/rules/cloud/aws/aws/waf_shield_rule_modifications.yml +++ b/rules/cloud/aws/aws/waf_shield_rule_modifications.yml @@ -1,18 +1,17 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws +- aws name: AWS WAF and Shield Rule Modifications impact: confidentiality: 2 integrity: 3 availability: 3 category: Defense Evasion -technique: "T1562.001 - Impair Defenses: Disable or Modify Tools" +technique: 'T1562.001 - Impair Defenses: Disable or Modify Tools' adversary: origin references: - - https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html - - https://attack.mitre.org/techniques/T1562/001/ +- https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html +- https://attack.mitre.org/techniques/T1562/001/ description: | Detects modifications or deletions of AWS WAF web ACLs and rules which could be used to remove web application protections before launching an attack. Attackers may disable WAF rules to allow malicious traffic or remove rate limiting protections. @@ -26,9 +25,14 @@ description: | 7. Implement IAM policies to restrict WAF modification access 8. Enable AWS Config rules to monitor WAF configuration compliance where: | - (equals("log.eventSource", "wafv2.amazonaws.com") || equals("log.eventSource", "waf.amazonaws.com")) && + equals("log.awsRecordType","cloudtrail") && + ((equals("log.eventSource", "wafv2.amazonaws.com") || + equals("log.eventSource", "waf.amazonaws.com")) && oneOf("log.eventName", ["DeleteWebACL", "DeleteRule", "DeleteRuleGroup", "UpdateWebACL", "DeleteIPSet", "DeleteRegexPatternSet", "DisassociateWebACL"]) && - equals("log.errorCode", "") + equals("actionResult","success")) groupBy: - - adversary.user - - lastEvent.log.eventName +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.user +- lastEvent.log.eventName diff --git a/rules/cloud/aws/credential_access_aws_iam_assume_role_brute_force.yml b/rules/cloud/aws/credential_access_aws_iam_assume_role_brute_force.yml index bfb7daafc..b0953b071 100644 --- a/rules/cloud/aws/credential_access_aws_iam_assume_role_brute_force.yml +++ b/rules/cloud/aws/credential_access_aws_iam_assume_role_brute_force.yml @@ -1,35 +1,61 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS IAM Brute Force of Assume Role Policy" +- aws +name: Repeated Malformed AWS IAM Trust Policy Updates impact: confidentiality: 3 integrity: 2 availability: 1 -category: "Credential Access" -technique: "T1110 - Brute Force" +category: Privilege Escalation +technique: T1098 - Account Manipulation adversary: origin -description: "Identifies a high number of failed attempts to assume an AWS Identity and Access Management (IAM) role. - IAM roles are used to delegate access to users or services. An adversary may attempt to enumerate IAM - roles in order to determine if a role exists before attempting to assume or hijack the discovered role" +description: | + Detects repeated UpdateAssumeRolePolicy requests rejected with MalformedPolicyDocument or MalformedPolicyDocumentException by the same actor and account. This is a rejected trust-policy update, not a failed AssumeRole authentication or password brute force. Review the proposed policy and administrative workflow. references: - - "https://attack.mitre.org/tactics/TA0006/" - - "https://attack.mitre.org/techniques/T1110/" - - "https://www.praetorian.com/blog/aws-iam-assume-role-vulnerabilities" - - "https://rhinosecuritylabs.com/aws/assume-worst-aws-assume-role-enumeration/" +- https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateAssumeRolePolicy.html +- https://attack.mitre.org/techniques/T1098/ where: | - equals("log.eventSource", "iam.amazonaws.com") && - equals("log.eventName", "UpdateAssumeRolePolicy") && - equals("log.errorCode", "MalformedPolicyDocumentException") + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","iam.amazonaws.com") && + oneOf("log.eventName",["UpdateAssumeRolePolicy"]) && + oneOf("log.errorCode",["MalformedPolicyDocument", "MalformedPolicyDocumentException"]) && + equals("actionResult","failure"))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("log.awsActorKeyType") && + !equals("log.awsActorKeyType","") && + exists("log.awsActorKey") && + !equals("log.awsActorKey","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: origin.user - operator: filter_term - value: '{{.origin.user}}' - within: 15m - count: 5 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: log.awsActorKeyType + operator: filter_term + value: '{{.log.awsActorKeyType}}' + - field: log.awsActorKey + operator: filter_term + value: '{{.log.awsActorKey}}' + - field: log.correlationCandidate.credential_access_aws_iam_assume_role_brute_force + operator: filter_term + value: match + within: 15m + count: 5 groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/credential_access_root_console_failure_brute_force.yml b/rules/cloud/aws/credential_access_root_console_failure_brute_force.yml index b861ed7d5..ee4f951cf 100644 --- a/rules/cloud/aws/credential_access_root_console_failure_brute_force.yml +++ b/rules/cloud/aws/credential_access_root_console_failure_brute_force.yml @@ -1,33 +1,58 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS Management Console Brute Force of Root User Identity" +- aws +name: AWS Management Console Brute Force of Root User Identity impact: confidentiality: 3 integrity: 2 availability: 1 -category: "Credential Access" -technique: "T1110 - Brute Force" +category: Credential Access +technique: T1110 - Brute Force adversary: origin -description: "Identifies a high number of failed authentication attempts to the AWS management console for the Root user identity. An adversary may attempt to brute force the password for the Root user identity, as it has complete access to all services and resources for the AWS account" +description: Identifies a high number of failed authentication attempts to the AWS management console for the Root user identity. + An adversary may attempt to brute force the password for the Root user identity, as it has complete access to all services + and resources for the AWS account references: - - "https://attack.mitre.org/tactics/TA0006/" - - "https://attack.mitre.org/techniques/T1110/" - - "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html" +- https://attack.mitre.org/tactics/TA0006/ +- https://attack.mitre.org/techniques/T1110/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html where: | - equals("log.eventSource", "signin.amazonaws.com") && - equals("log.eventName", "ConsoleLogin") && - equals("log.userIdentityType", "root") && - (exists("log.errorCode") || exists("log.errorMessage")) + (equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource","signin.amazonaws.com") && + oneOf("log.eventName",["ConsoleLogin"]) && + equals("log.userIdentity.type","Root") && + oneOf("actionResult",["failure", "denied"]))) && + exists("dataSource") && + !equals("dataSource","") && + exists("log.awsAccountKeyType") && + !equals("log.awsAccountKeyType","") && + exists("log.awsAccountKey") && + !equals("log.awsAccountKey","") && + exists("origin.ip") && + !equals("origin.ip","") afterEvents: - - indexPattern: v11-log-aws-* - with: - - field: origin.ip - operator: filter_term - value: '{{.origin.ip}}' - within: 15m - count: 5 +- indexPattern: v11-log-aws-* + with: + - field: dataSource + operator: filter_term + value: '{{.dataSource}}' + - field: log.awsAccountKeyType + operator: filter_term + value: '{{.log.awsAccountKeyType}}' + - field: log.awsAccountKey + operator: filter_term + value: '{{.log.awsAccountKey}}' + - field: origin.ip + operator: filter_term + value: '{{.origin.ip}}' + - field: log.correlationCandidate.credential_access_root_console_failure_brute_force + operator: filter_term + value: match + within: 15m + count: 5 groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_cloudtrail_logging_deleted.yml b/rules/cloud/aws/defense_evasion_cloudtrail_logging_deleted.yml index 8b6b58ace..ddabe17e5 100644 --- a/rules/cloud/aws/defense_evasion_cloudtrail_logging_deleted.yml +++ b/rules/cloud/aws/defense_evasion_cloudtrail_logging_deleted.yml @@ -1,24 +1,28 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS CloudTrail Log Deleted" +- aws +name: AWS CloudTrail Log Deleted impact: confidentiality: 2 integrity: 3 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies the deletion of an AWS log trail. An adversary may delete trails in an attempt to evade defenses" +description: Identifies the deletion of an AWS log trail. An adversary may delete trails in an attempt to evade defenses references: - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_DeleteTrail.html" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/delete-trail.html" +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_DeleteTrail.html +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/delete-trail.html where: | - equals("log.eventSource", "cloudtrail.amazonaws.com") && - equals("log.eventName", "DeleteTrail") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "cloudtrail.amazonaws.com") && + equals("log.eventName", "DeleteTrail") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_cloudtrail_logging_suspended.yml b/rules/cloud/aws/defense_evasion_cloudtrail_logging_suspended.yml index aa9a6e163..a7d5fdda6 100644 --- a/rules/cloud/aws/defense_evasion_cloudtrail_logging_suspended.yml +++ b/rules/cloud/aws/defense_evasion_cloudtrail_logging_suspended.yml @@ -1,24 +1,29 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS CloudTrail Log Suspended" +- aws +name: AWS CloudTrail Log Suspended impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies suspending the recording of AWS API calls and log file delivery for the specified trail. An adversary may suspend trails in an attempt to evade defenses" +description: Identifies suspending the recording of AWS API calls and log file delivery for the specified trail. An adversary + may suspend trails in an attempt to evade defenses references: - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_StopLogging.html" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/stop-logging.html" +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_StopLogging.html +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/stop-logging.html where: | - equals("log.eventSource", "cloudtrail.amazonaws.com") && - equals("log.eventName", "StopLogging") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "cloudtrail.amazonaws.com") && + equals("log.eventName", "StopLogging") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_config_service_rule_deletion.yml b/rules/cloud/aws/defense_evasion_config_service_rule_deletion.yml index b5833d85e..ef4ca3e72 100644 --- a/rules/cloud/aws/defense_evasion_config_service_rule_deletion.yml +++ b/rules/cloud/aws/defense_evasion_config_service_rule_deletion.yml @@ -1,28 +1,32 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS Config Service Tampering" +- aws +name: AWS Config Service Tampering impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies attempts to delete an AWS Config Service resource. An adversary may tamper with Config services in order to reduce visibility into the security posture of an account and / or its workload instances" +description: Identifies attempts to delete an AWS Config Service resource. An adversary may tamper with Config services in + order to reduce visibility into the security posture of an account and / or its workload instances references: - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://docs.aws.amazon.com/config/latest/developerguide/how-does-config-work.html" - - "https://docs.aws.amazon.com/config/latest/APIReference/API_Operations.html" +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://docs.aws.amazon.com/config/latest/developerguide/how-does-config-work.html +- https://docs.aws.amazon.com/config/latest/APIReference/API_Operations.html where: | - equals("log.eventSource", "config.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "config.amazonaws.com") && oneOf("log.eventName", ["DeleteConfigRule", "DeleteOrganizationConfigRule", "DeleteConfigurationAggregator", "DeleteConfigurationRecorder", "DeleteConformancePack", "DeleteOrganizationConformancePack", "DeleteDeliveryChannel", "DeleteRemediationConfiguration", - "DeleteRetentionConfiguration"]) + "DeleteRetentionConfiguration"])) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_configuration_recorder_stopped.yml b/rules/cloud/aws/defense_evasion_configuration_recorder_stopped.yml index a60c1885c..5a64908d5 100644 --- a/rules/cloud/aws/defense_evasion_configuration_recorder_stopped.yml +++ b/rules/cloud/aws/defense_evasion_configuration_recorder_stopped.yml @@ -1,24 +1,28 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS Configuration Recorder Stopped" +- aws +name: AWS Configuration Recorder Stopped impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies an AWS configuration change to stop recording a designated set of resources" +description: Identifies an AWS configuration change to stop recording a designated set of resources references: - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/configservice/stop-configuration-recorder.html" - - "https://docs.aws.amazon.com/config/latest/APIReference/API_StopConfigurationRecorder.html" - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/configservice/stop-configuration-recorder.html +- https://docs.aws.amazon.com/config/latest/APIReference/API_StopConfigurationRecorder.html +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ where: | - equals("log.eventSource", "config.amazonaws.com") && - equals("log.eventName", "StopConfigurationRecorder") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "config.amazonaws.com") && + equals("log.eventName", "StopConfigurationRecorder") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_ec2_flow_log_deletion.yml b/rules/cloud/aws/defense_evasion_ec2_flow_log_deletion.yml index 33da62b3a..22ec530a8 100644 --- a/rules/cloud/aws/defense_evasion_ec2_flow_log_deletion.yml +++ b/rules/cloud/aws/defense_evasion_ec2_flow_log_deletion.yml @@ -1,24 +1,29 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS EC2 Flow Log Deletion" +- aws +name: AWS EC2 Flow Log Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies the deletion of one or more flow logs in AWS Elastic Compute Cloud (EC2). An adversary may delete flow logs in an attempt to evade defenses" +description: Identifies the deletion of one or more flow logs in AWS Elastic Compute Cloud (EC2). An adversary may delete + flow logs in an attempt to evade defenses references: - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/delete-flow-logs.html" - - "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteFlowLogs.html" - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/delete-flow-logs.html +- https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteFlowLogs.html +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "DeleteFlowLogs") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ec2.amazonaws.com") && + equals("log.eventName", "DeleteFlowLogs") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_guardduty_detector_deletion.yml b/rules/cloud/aws/defense_evasion_guardduty_detector_deletion.yml index a6904e3f9..336de461f 100644 --- a/rules/cloud/aws/defense_evasion_guardduty_detector_deletion.yml +++ b/rules/cloud/aws/defense_evasion_guardduty_detector_deletion.yml @@ -1,24 +1,29 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS GuardDuty Detector Deletion" +- aws +name: AWS GuardDuty Detector Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies the deletion of an Amazon GuardDuty detector. Upon deletion, GuardDuty stops monitoring the environment and all existing findings are lost" +description: Identifies the deletion of an Amazon GuardDuty detector. Upon deletion, GuardDuty stops monitoring the environment + and all existing findings are lost references: - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/guardduty/delete-detector.html" - - "https://docs.aws.amazon.com/guardduty/latest/APIReference/API_DeleteDetector.html" +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/guardduty/delete-detector.html +- https://docs.aws.amazon.com/guardduty/latest/APIReference/API_DeleteDetector.html where: | - equals("log.eventSource", "guardduty.amazonaws.com") && - equals("log.eventName", "DeleteDetector") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "guardduty.amazonaws.com") && + equals("log.eventName", "DeleteDetector") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_s3_bucket_configuration_deletion.yml b/rules/cloud/aws/defense_evasion_s3_bucket_configuration_deletion.yml index 1470e3fa5..e4cd8dcd9 100644 --- a/rules/cloud/aws/defense_evasion_s3_bucket_configuration_deletion.yml +++ b/rules/cloud/aws/defense_evasion_s3_bucket_configuration_deletion.yml @@ -1,28 +1,32 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS S3 Bucket Configuration Deletion" +- aws +name: AWS S3 Bucket Configuration Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1070 - Indicator Removal" +category: Defense Evasion +technique: T1070 - Indicator Removal adversary: origin -description: "Identifies the deletion of various Amazon Simple Storage Service (S3) bucket configuration components" +description: Identifies the deletion of various Amazon Simple Storage Service (S3) bucket configuration components references: - - "https://attack.mitre.org/techniques/T1070/" - - "https://attack.mitre.org/tactics/TA0005/" - - "https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketPolicy.html" - - "https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketReplication.html" - - "https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html" - - "https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketEncryption.html" - - "https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html" +- https://attack.mitre.org/techniques/T1070/ +- https://attack.mitre.org/tactics/TA0005/ +- https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketPolicy.html +- https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketReplication.html +- https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html +- https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketEncryption.html +- https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html where: | - equals("log.eventSource", "s3.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "s3.amazonaws.com") && oneOf("log.eventName", ["DeleteBucketPolicy", "DeleteBucketReplication", - "DeleteBucketCors", "DeleteBucketEncryption", "DeleteBucketLifecycle"]) + "DeleteBucketCors", "DeleteBucketEncryption", "DeleteBucketLifecycle"]) && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_waf_acl_deletion.yml b/rules/cloud/aws/defense_evasion_waf_acl_deletion.yml index d6d535560..4785b48e3 100644 --- a/rules/cloud/aws/defense_evasion_waf_acl_deletion.yml +++ b/rules/cloud/aws/defense_evasion_waf_acl_deletion.yml @@ -1,24 +1,28 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS WAF Access Control List Deletion" +- aws +name: AWS WAF Access Control List Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies the deletion of a specified AWS Web Application Firewall (WAF) access control list" +description: Identifies the deletion of a specified AWS Web Application Firewall (WAF) access control list references: - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/waf-regional/delete-web-acl.html" - - "https://docs.aws.amazon.com/waf/latest/APIReference/API_wafRegional_DeleteWebACL.html" +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/waf-regional/delete-web-acl.html +- https://docs.aws.amazon.com/waf/latest/APIReference/API_wafRegional_DeleteWebACL.html where: | - oneOf("log.eventSource", ["waf.amazonaws.com", "waf-regional.amazonaws.com", "wafv2.amazonaws.com"]) && - equals("log.eventName", "DeleteWebACL") + equals("log.awsRecordType","cloudtrail") && + (oneOf("log.eventSource", ["waf.amazonaws.com", "waf-regional.amazonaws.com", "wafv2.amazonaws.com"]) && + equals("log.eventName", "DeleteWebACL") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/defense_evasion_waf_rule_or_rule_group_deletion.yml b/rules/cloud/aws/defense_evasion_waf_rule_or_rule_group_deletion.yml index 8d08c5e9d..522f3c0d1 100644 --- a/rules/cloud/aws/defense_evasion_waf_rule_or_rule_group_deletion.yml +++ b/rules/cloud/aws/defense_evasion_waf_rule_or_rule_group_deletion.yml @@ -1,24 +1,29 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS WAF Rule or Rule Group Deletion" +- aws +name: AWS WAF Rule or Rule Group Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Defense Evasion" -technique: "T1562 - Impair Defenses" +category: Defense Evasion +technique: T1562 - Impair Defenses adversary: origin -description: "Identifies the deletion of a specified AWS Web Application Firewall (WAF) rule or rule group" +description: Identifies the deletion of a specified AWS Web Application Firewall (WAF) rule or rule group references: - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/waf/delete-rule-group.html" - - "https://docs.aws.amazon.com/waf/latest/APIReference/API_waf_DeleteRuleGroup.html" +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/waf/delete-rule-group.html +- https://docs.aws.amazon.com/waf/latest/APIReference/API_waf_DeleteRuleGroup.html where: | - oneOf("log.eventSource", ["waf.amazonaws.com", "waf-regional.amazonaws.com", "wafv2.amazonaws.com"]) && - (equals("log.eventName", "DeleteRule") || equals("log.eventName", "DeleteRuleGroup")) + equals("log.awsRecordType","cloudtrail") && + (oneOf("log.eventSource", ["waf.amazonaws.com", "waf-regional.amazonaws.com", "wafv2.amazonaws.com"]) && + (equals("log.eventName", "DeleteRule") || + equals("log.eventName", "DeleteRuleGroup")) && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/exfiltration_ec2_full_network_packet_capture_detected.yml b/rules/cloud/aws/exfiltration_ec2_full_network_packet_capture_detected.yml index b82737d5a..507d44e24 100644 --- a/rules/cloud/aws/exfiltration_ec2_full_network_packet_capture_detected.yml +++ b/rules/cloud/aws/exfiltration_ec2_full_network_packet_capture_detected.yml @@ -1,29 +1,34 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS EC2 Full Network Packet Capture Detected" +- aws +name: AWS EC2 Traffic Mirroring Configuration Request impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Exfiltration" -technique: "T1020 - Automated Exfiltration" +category: Exfiltration +technique: T1020 - Automated Exfiltration adversary: origin -description: "Identifies potential Traffic Mirroring in an Amazon Elastic Compute Cloud (EC2) instance. Traffic Mirroring is an Amazon VPC feature that you can use to copy network traffic from an elastic network interface. This feature can potentially be abused to exfiltrate sensitive data from unencrypted internal traffic" +description: | + Identifies a successful request to create a traffic mirror filter, rule, session or target. Inspect the configuration and destination to determine whether packet mirroring is active and authorized; creating a filter alone does not prove that traffic was captured. references: - - "https://attack.mitre.org/tactics/TA0010/" - - "https://attack.mitre.org/techniques/T1020/" - - "https://attack.mitre.org/tactics/TA0009/" - - "https://attack.mitre.org/techniques/T1074/" - - "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_TrafficMirrorFilter.html" - - "https://github.com/easttimor/aws-incident-response" +- https://attack.mitre.org/tactics/TA0010/ +- https://attack.mitre.org/techniques/T1020/ +- https://attack.mitre.org/tactics/TA0009/ +- https://attack.mitre.org/techniques/T1074/ +- https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_TrafficMirrorFilter.html +- https://github.com/easttimor/aws-incident-response where: | - equals("log.eventSource", "ec2.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ec2.amazonaws.com") && (equals("log.eventName", "CreateTrafficMirrorFilter") || equals("log.eventName", "CreateTrafficMirrorFilterRule") || equals("log.eventName", "CreateTrafficMirrorSession") || - equals("log.eventName", "CreateTrafficMirrorTarget")) + equals("log.eventName", "CreateTrafficMirrorTarget")) && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/exfiltration_ec2_snapshot_change_activity.yml b/rules/cloud/aws/exfiltration_ec2_snapshot_change_activity.yml index 6b13455df..2fa3fc1b7 100644 --- a/rules/cloud/aws/exfiltration_ec2_snapshot_change_activity.yml +++ b/rules/cloud/aws/exfiltration_ec2_snapshot_change_activity.yml @@ -1,24 +1,29 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS EC2 Snapshot Activity" +- aws +name: AWS EC2 Snapshot Activity impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Exfiltration" -technique: "T1537 - Transfer Data to Cloud Account" +category: Exfiltration +technique: T1537 - Transfer Data to Cloud Account adversary: origin -description: "An attempt was made to modify AWS EC2 snapshot attributes. Snapshots are sometimes shared by threat actors in order to exfiltrate bulk data from an EC2 fleet. If the permissions were modified, verify the snapshot was not shared with an unauthorized or unexpected AWS account" +description: An attempt was made to modify AWS EC2 snapshot attributes. Snapshots are sometimes shared by threat actors in + order to exfiltrate bulk data from an EC2 fleet. If the permissions were modified, verify the snapshot was not shared with + an unauthorized or unexpected AWS account references: - - "https://attack.mitre.org/tactics/TA0010/" - - "https://attack.mitre.org/techniques/T1537/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/modify-snapshot-attribute.html" - - "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifySnapshotAttribute.html" +- https://attack.mitre.org/tactics/TA0010/ +- https://attack.mitre.org/techniques/T1537/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/modify-snapshot-attribute.html +- https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifySnapshotAttribute.html where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "ModifySnapshotAttribute") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ec2.amazonaws.com") && + equals("log.eventName", "ModifySnapshotAttribute")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/exfiltration_ec2_vm_export_failure.yml b/rules/cloud/aws/exfiltration_ec2_vm_export_failure.yml index c55b4e884..64cece575 100644 --- a/rules/cloud/aws/exfiltration_ec2_vm_export_failure.yml +++ b/rules/cloud/aws/exfiltration_ec2_vm_export_failure.yml @@ -1,25 +1,29 @@ -# Rule version v1.0.3 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - "aws" -name: "AWS EC2 VM Export Failure" +- aws +name: AWS EC2 VM Export Request impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Exfiltration" -technique: "T1537 - Transfer Data to Cloud Account" +category: Exfiltration +technique: T1537 - Transfer Data to Cloud Account adversary: origin -description: "Identifies an attempt to export an AWS EC2 instance. A virtual machine (VM) export may indicate an attempt to extract or exfiltrate information" +description: | + Identifies a CreateInstanceExportTask request, including rejected requests. Check actionResult and the vendor error fields to determine whether AWS accepted it. This event does not prove that an export completed or that data was exfiltrated. references: - - "https://attack.mitre.org/techniques/T1537/" - - "https://attack.mitre.org/tactics/TA0010/" - - "https://attack.mitre.org/tactics/TA0009/" - - "https://attack.mitre.org/techniques/T1005/" - - "https://docs.aws.amazon.com/vm-import/latest/userguide/vmexport.html#export-instance" +- https://attack.mitre.org/techniques/T1537/ +- https://attack.mitre.org/tactics/TA0010/ +- https://attack.mitre.org/tactics/TA0009/ +- https://attack.mitre.org/techniques/T1005/ +- https://docs.aws.amazon.com/vm-import/latest/userguide/vmexport.html#export-instance where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "CreateInstanceExportTask") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ec2.amazonaws.com") && + equals("log.eventName", "CreateInstanceExportTask")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/exfiltration_rds_snapshot_export.yml b/rules/cloud/aws/exfiltration_rds_snapshot_export.yml index b16eeaffc..65f5d15cc 100644 --- a/rules/cloud/aws/exfiltration_rds_snapshot_export.yml +++ b/rules/cloud/aws/exfiltration_rds_snapshot_export.yml @@ -1,22 +1,27 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS RDS Snapshot Export" +- aws +name: AWS RDS Snapshot Export Request Accepted impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Exfiltration" -technique: "T1537 - Transfer Data to Cloud Account" +category: Exfiltration +technique: T1537 - Transfer Data to Cloud Account adversary: origin -description: "Identifies the export of an Amazon Relational Database Service (RDS) Aurora database snapshot" +description: | + Identifies a successful StartExportTask API request. Review the export destination and task status; API acceptance does not establish that the export completed. references: - - "https://attack.mitre.org/tactics/TA0010/" - - "https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_StartExportTask.html" +- https://attack.mitre.org/tactics/TA0010/ +- https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_StartExportTask.html where: | - equals("log.eventSource", "rds.amazonaws.com") && - equals("log.eventName", "StartExportTask") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "rds.amazonaws.com") && + equals("log.eventName", "StartExportTask") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/impact_cloudtrail_logging_updated.yml b/rules/cloud/aws/impact_cloudtrail_logging_updated.yml index d29c9759d..254601b81 100644 --- a/rules/cloud/aws/impact_cloudtrail_logging_updated.yml +++ b/rules/cloud/aws/impact_cloudtrail_logging_updated.yml @@ -1,26 +1,30 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS CloudTrail Log Updated" +- aws +name: AWS CloudTrail Log Updated impact: confidentiality: 2 integrity: 2 availability: 1 -category: "Impact" -technique: "T1565 - Data Manipulation" +category: Impact +technique: T1565 - Data Manipulation adversary: origin -description: "Identifies an update to an AWS log trail setting that specifies the delivery of log files" +description: Identifies an update to an AWS log trail setting that specifies the delivery of log files references: - - "https://attack.mitre.org/tactics/TA0040/" - - "https://attack.mitre.org/techniques/T1565/" - - "https://attack.mitre.org/tactics/TA0009/" - - "https://attack.mitre.org/techniques/T1530/" - - "https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_UpdateTrail.html" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/update-trail.html" +- https://attack.mitre.org/tactics/TA0040/ +- https://attack.mitre.org/techniques/T1565/ +- https://attack.mitre.org/tactics/TA0009/ +- https://attack.mitre.org/techniques/T1530/ +- https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_UpdateTrail.html +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/update-trail.html where: | - equals("log.eventSource", "cloudtrail.amazonaws.com") && - equals("log.eventName", "UpdateTrail") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "cloudtrail.amazonaws.com") && + equals("log.eventName", "UpdateTrail") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/impact_cloudwatch_log_group_deletion.yml b/rules/cloud/aws/impact_cloudwatch_log_group_deletion.yml index 8dfd51284..76143911c 100644 --- a/rules/cloud/aws/impact_cloudwatch_log_group_deletion.yml +++ b/rules/cloud/aws/impact_cloudwatch_log_group_deletion.yml @@ -1,26 +1,31 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS CloudWatch Log Group Deletion" +- aws +name: AWS CloudWatch Log Group Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Impact" -technique: "T1485 - Data Destruction" +category: Impact +technique: T1485 - Data Destruction adversary: origin -description: "Identifies the deletion of a specified AWS CloudWatch log group. When a log group is deleted, all the archived log events associated with the log group are also permanently deleted" +description: Identifies the deletion of a specified AWS CloudWatch log group. When a log group is deleted, all the archived + log events associated with the log group are also permanently deleted references: - - "https://attack.mitre.org/tactics/TA0040/" - - "https://attack.mitre.org/techniques/T1485/" - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/delete-log-group.html" - - "https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DeleteLogGroup.html" +- https://attack.mitre.org/tactics/TA0040/ +- https://attack.mitre.org/techniques/T1485/ +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/delete-log-group.html +- https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DeleteLogGroup.html where: | - equals("log.eventSource", "logs.amazonaws.com") && - equals("log.eventName", "DeleteLogGroup") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "logs.amazonaws.com") && + equals("log.eventName", "DeleteLogGroup") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/impact_cloudwatch_log_stream_deletion.yml b/rules/cloud/aws/impact_cloudwatch_log_stream_deletion.yml index 20ec1c1f0..6060d68f2 100644 --- a/rules/cloud/aws/impact_cloudwatch_log_stream_deletion.yml +++ b/rules/cloud/aws/impact_cloudwatch_log_stream_deletion.yml @@ -1,27 +1,31 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS CloudWatch Log Stream Deletion" +- aws +name: AWS CloudWatch Log Stream Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Impact" -technique: "T1485 - Data Destruction" +category: Impact +technique: T1485 - Data Destruction adversary: origin -description: "Identifies the deletion of an AWS CloudWatch log stream, which permanently deletes all associated archived log events with the stream" +description: Identifies the deletion of an AWS CloudWatch log stream, which permanently deletes all associated archived log + events with the stream references: - - "https://attack.mitre.org/tactics/TA0040/" - - "https://attack.mitre.org/techniques/T1485/" - - "https://attack.mitre.org/tactics/TA0005/" - - "https://attack.mitre.org/techniques/T1562/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/delete-log-stream.html" - - "https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DeleteLogStream.html" - +- https://attack.mitre.org/tactics/TA0040/ +- https://attack.mitre.org/techniques/T1485/ +- https://attack.mitre.org/tactics/TA0005/ +- https://attack.mitre.org/techniques/T1562/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/delete-log-stream.html +- https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DeleteLogStream.html where: | - equals("log.eventSource", "logs.amazonaws.com") && - equals("log.eventName", "DeleteLogStream") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "logs.amazonaws.com") && + equals("log.eventName", "DeleteLogStream") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/impact_ec2_disable_ebs_encryption.yml b/rules/cloud/aws/impact_ec2_disable_ebs_encryption.yml index 183c4e3b8..c4b367d74 100644 --- a/rules/cloud/aws/impact_ec2_disable_ebs_encryption.yml +++ b/rules/cloud/aws/impact_ec2_disable_ebs_encryption.yml @@ -1,25 +1,30 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS EC2 Encryption Disabled" +- aws +name: AWS EC2 Encryption Disabled impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Impact" -technique: "T1565 - Data Manipulation" +category: Impact +technique: T1565 - Data Manipulation adversary: origin -description: "Identifies disabling of Amazon Elastic Block Store (EBS) encryption by default in the current region. Disabling encryption by default does not change the encryption status of your existing volumes" +description: Identifies disabling of Amazon Elastic Block Store (EBS) encryption by default in the current region. Disabling + encryption by default does not change the encryption status of your existing volumes references: - - "https://attack.mitre.org/tactics/TA0040/" - - "https://attack.mitre.org/techniques/T1565/" - - "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/disable-ebs-encryption-by-default.html" - - "https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DisableEbsEncryptionByDefault.html" +- https://attack.mitre.org/tactics/TA0040/ +- https://attack.mitre.org/techniques/T1565/ +- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/disable-ebs-encryption-by-default.html +- https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DisableEbsEncryptionByDefault.html where: | - equals("log.eventSource", "ec2.amazonaws.com") && - equals("log.eventName", "DisableEbsEncryptionByDefault") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ec2.amazonaws.com") && + equals("log.eventName", "DisableEbsEncryptionByDefault") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/impact_iam_deactivate_mfa_device.yml b/rules/cloud/aws/impact_iam_deactivate_mfa_device.yml index 62d18211a..0bbaa26e5 100644 --- a/rules/cloud/aws/impact_iam_deactivate_mfa_device.yml +++ b/rules/cloud/aws/impact_iam_deactivate_mfa_device.yml @@ -1,25 +1,30 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS IAM Deactivation of MFA Device" +- aws +name: AWS IAM Deactivation of MFA Device impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Impact" -technique: "T1531 - Account Access Removal" +category: Impact +technique: T1531 - Account Access Removal adversary: origin -description: "Identifies the deactivation of a specified multi-factor authentication (MFA) device and removes it from association with the user name for which it was originally enabled. In AWS Identity and Access Management (IAM), a device must be deactivated before it can be deleted" +description: Identifies the deactivation of a specified multi-factor authentication (MFA) device and removes it from association + with the user name for which it was originally enabled. In AWS Identity and Access Management (IAM), a device must be deactivated + before it can be deleted references: - - "https://attack.mitre.org/tactics/TA0040/" - - "https://attack.mitre.org/techniques/T1531/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/deactivate-mfa-device.html" - - "https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeactivateMFADevice.html" - +- https://attack.mitre.org/tactics/TA0040/ +- https://attack.mitre.org/techniques/T1531/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/deactivate-mfa-device.html +- https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeactivateMFADevice.html where: | - equals("log.eventSource", "iam.amazonaws.com") && - oneOf("log.eventName", ["DeactivateMFADevice", "DeleteVirtualMFADevice"]) + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "iam.amazonaws.com") && + oneOf("log.eventName", ["DeactivateMFADevice", "DeleteVirtualMFADevice"]) && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/impact_rds_cluster_deletion.yml b/rules/cloud/aws/impact_rds_cluster_deletion.yml index 32cd37402..5e34f8322 100644 --- a/rules/cloud/aws/impact_rds_cluster_deletion.yml +++ b/rules/cloud/aws/impact_rds_cluster_deletion.yml @@ -1,27 +1,31 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS RDS Cluster Deletion" +- aws +name: AWS RDS Cluster Deletion impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Impact" -technique: "T1485 - Data Destruction" +category: Impact +technique: T1485 - Data Destruction adversary: origin -description: "Identifies the deletion of an Amazon Relational Database Service (RDS) Aurora database cluster or global database cluster" +description: Identifies the deletion of an Amazon Relational Database Service (RDS) Aurora database cluster or global database + cluster references: - - "https://attack.mitre.org/tactics/TA0040/" - - "https://attack.mitre.org/techniques/T1485/" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/delete-db-cluster.html" - - "https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DeleteDBCluster.html" - - "https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/delete-global-cluster.html" - - "https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DeleteGlobalCluster.html" - +- https://attack.mitre.org/tactics/TA0040/ +- https://attack.mitre.org/techniques/T1485/ +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/delete-db-cluster.html +- https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DeleteDBCluster.html +- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/delete-global-cluster.html +- https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DeleteGlobalCluster.html where: | - equals("log.eventSource", "rds.amazonaws.com") && - oneOf("log.eventName", ["DeleteDBCluster", "DeleteGlobalCluster"]) + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "rds.amazonaws.com") && + oneOf("log.eventName", ["DeleteDBCluster", "DeleteGlobalCluster"]) && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/initial_access_console_login_root.yml b/rules/cloud/aws/initial_access_console_login_root.yml index 5b51b12a6..8a7a97b2b 100644 --- a/rules/cloud/aws/initial_access_console_login_root.yml +++ b/rules/cloud/aws/initial_access_console_login_root.yml @@ -1,24 +1,34 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS Management Console Root Login" +- aws +name: AWS Management Console Root Login impact: confidentiality: 3 integrity: 3 availability: 3 -category: "Initial Access" -technique: "T1078 - Valid Accounts" +category: Initial Access +technique: T1078 - Valid Accounts adversary: origin -description: "Identifies a successful login to the AWS Management Console by the Root user.
Adversaries may obtain and abuse credentials of a cloud account as a means of gaining Initial Access, Persistence, Privilege Escalation, or Defense Evasion.
Compromised credentials for cloud accounts can be used to harvest sensitive data from online storage accounts and databases.
Potential false positives
It’s strongly recommended that the root user is not used for everyday tasks, including the administrative ones. Verify whether the IP address, location, and/or hostname should be logging in as root in your environment. Unfamiliar root logins should be investigated immediately. If known behavior is causing false positives, it can be exempted from the rule." +description: "Identifies a successful login to the AWS Management Console by the Root user.
Adversaries may obtain and\ + \ abuse credentials of a cloud account as a means of gaining Initial Access, Persistence, Privilege Escalation, or Defense\ + \ Evasion.
Compromised credentials for cloud accounts can be used to harvest sensitive data from online storage accounts\ + \ and databases.
Potential false positives
It\u2019s strongly recommended that the root user is not\ + \ used for everyday tasks, including the administrative ones. Verify whether the IP address, location, and/or hostname should\ + \ be logging in as root in your environment. Unfamiliar root logins should be investigated immediately. If known behavior\ + \ is causing false positives, it can be exempted from the rule." references: - - "https://attack.mitre.org/tactics/TA0001/" - - "https://attack.mitre.org/techniques/T1078/" - - "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html" +- https://attack.mitre.org/tactics/TA0001/ +- https://attack.mitre.org/techniques/T1078/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html where: | - equals("log.eventSource", "signin.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "signin.amazonaws.com") && equals("log.eventName", "ConsoleLogin") && - equals("log.userIdentityType", "root") + equals("log.userIdentity.type", "Root") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/initial_access_password_recovery.yml b/rules/cloud/aws/initial_access_password_recovery.yml index 7da2aa045..fa8f1ae39 100644 --- a/rules/cloud/aws/initial_access_password_recovery.yml +++ b/rules/cloud/aws/initial_access_password_recovery.yml @@ -1,23 +1,29 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS IAM Password Recovery Requested" +- aws +name: AWS IAM Password Recovery Requested impact: confidentiality: 2 integrity: 1 availability: 0 -category: "Initial Access" -technique: "T1078 - Valid Accounts" +category: Initial Access +technique: T1078 - Valid Accounts adversary: origin -description: "Identifies AWS IAM password recovery requests. An adversary may attempt to gain unauthorized AWS access by abusing password recovery mechanisms.
Potential false positives
Verify whether the user identity, user agent, and/or hostname should be requesting changes in your environment. Password reset attempts from unfamiliar users should be investigated. If known behavior is causing false positives, it can be exempted from the rule." +description: Identifies AWS IAM password recovery requests. An adversary may attempt to gain unauthorized AWS access by abusing + password recovery mechanisms.
Potential false positives
Verify whether the user identity, user agent, + and/or hostname should be requesting changes in your environment. Password reset attempts from unfamiliar users should be + investigated. If known behavior is causing false positives, it can be exempted from the rule. references: - - "https://attack.mitre.org/tactics/TA0001/" - - "https://attack.mitre.org/techniques/T1078/" - - "https://www.cadosecurity.com/2020/06/11/an-ongoing-aws-phishing-campaign/" +- https://attack.mitre.org/tactics/TA0001/ +- https://attack.mitre.org/techniques/T1078/ +- https://www.cadosecurity.com/2020/06/11/an-ongoing-aws-phishing-campaign/ where: | - equals("log.eventSource", "signin.amazonaws.com") && - equals("log.eventName", "PasswordRecoveryRequested") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "signin.amazonaws.com") && + equals("log.eventName", "PasswordRecoveryRequested")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/initial_access_via_system_manager.yml b/rules/cloud/aws/initial_access_via_system_manager.yml index 060a99b7b..c73cfea6a 100644 --- a/rules/cloud/aws/initial_access_via_system_manager.yml +++ b/rules/cloud/aws/initial_access_via_system_manager.yml @@ -1,24 +1,28 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS Execution via System Manager" +- aws +name: AWS Systems Manager Command Request Accepted impact: confidentiality: 2 integrity: 1 availability: 1 -category: "Initial Access" -technique: "T1566 - Phishing" +category: Initial Access +technique: T1566 - Phishing adversary: origin -description: "Identifies the execution of commands and scripts via System Manager. Execution methods such as RunShellScript, RunPowerShellScript, and alike can be abused by an authenticated attacker to install a backdoor or to interact with a compromised instance via reverse-shell using system only commands
Potential false positives
Verify whether the user identity, user agent, and/or hostname should be making changes in your environment. Suspicious commands from unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule." +description: | + Identifies a successful SendCommand API request. Review the document, parameters and target instances for authorization, and inspect command execution results separately. API acceptance does not establish command execution success. references: - - "https://attack.mitre.org/tactics/TA0001/" - - "https://attack.mitre.org/techniques/T1566/" - - "https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-plugins.html" - +- https://attack.mitre.org/tactics/TA0001/ +- https://attack.mitre.org/techniques/T1566/ +- https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-plugins.html where: | - equals("log.eventSource", "ssm.amazonaws.com") && - equals("log.eventName", "SendCommand") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "ssm.amazonaws.com") && + equals("log.eventName", "SendCommand") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/persistence_route_53_domain_transfer_lock_disabled.yml b/rules/cloud/aws/persistence_route_53_domain_transfer_lock_disabled.yml index b305f9e9c..88c12f6de 100644 --- a/rules/cloud/aws/persistence_route_53_domain_transfer_lock_disabled.yml +++ b/rules/cloud/aws/persistence_route_53_domain_transfer_lock_disabled.yml @@ -1,25 +1,30 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS Route 53 Domain Transfer Lock Disabled" +- aws +name: AWS Route 53 Domain Transfer Lock Disabled impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Persistence" -technique: "T1098 - Account Manipulation" +category: Persistence +technique: T1098 - Account Manipulation adversary: origin -description: "Identifies when a transfer lock was removed from a Route 53 domain. It is recommended to refrain from performing this action unless intending to transfer the domain to a different registrar" +description: Identifies when a transfer lock was removed from a Route 53 domain. It is recommended to refrain from performing + this action unless intending to transfer the domain to a different registrar references: - - "https://attack.mitre.org/tactics/TA0003/" - - "https://attack.mitre.org/techniques/T1098/" - - "https://attack.mitre.org/tactics/TA0006/" - - "https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html" - - "https://docs.aws.amazon.com/Route53/latest/APIReference/API_domains_DisableDomainTransferLock.html" +- https://attack.mitre.org/tactics/TA0003/ +- https://attack.mitre.org/techniques/T1098/ +- https://attack.mitre.org/tactics/TA0006/ +- https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html +- https://docs.aws.amazon.com/Route53/latest/APIReference/API_domains_DisableDomainTransferLock.html where: | - equals("log.eventSource", "route53.amazonaws.com") && - equals("log.eventName", "DisableDomainTransferLock") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "route53domains.amazonaws.com") && + equalsIgnoreCase("log.eventName", "DisableDomainTransferLock") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/persistence_route_53_domain_transferred_to_another_account.yml b/rules/cloud/aws/persistence_route_53_domain_transferred_to_another_account.yml index 3d75cd70d..382201bae 100644 --- a/rules/cloud/aws/persistence_route_53_domain_transferred_to_another_account.yml +++ b/rules/cloud/aws/persistence_route_53_domain_transferred_to_another_account.yml @@ -1,24 +1,29 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS Route 53 Domain Transferred to Another Account" +- aws +name: AWS Route 53 Domain Transfer Request Accepted impact: confidentiality: 3 integrity: 3 availability: 3 -category: "Persistence" -technique: "T1098 - Account Manipulation" +category: Persistence +technique: T1098 - Account Manipulation adversary: origin -description: "Identifies when a request has been made to transfer a Route 53 domain to another AWS account" +description: | + Identifies an accepted Route 53 Domains request to transfer a domain to another AWS account. Check the destination account and transfer status; request acceptance does not establish that the transfer completed. references: - - "https://attack.mitre.org/tactics/TA0003/" - - "https://attack.mitre.org/techniques/T1098/" - - "https://attack.mitre.org/tactics/TA0006/" - - "https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html" +- https://attack.mitre.org/tactics/TA0003/ +- https://attack.mitre.org/techniques/T1098/ +- https://attack.mitre.org/tactics/TA0006/ +- https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html where: | - equals("log.eventSource", "route53.amazonaws.com") && - equals("log.eventName", "TransferDomainToAnotherAwsAccount") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "route53domains.amazonaws.com") && + equalsIgnoreCase("log.eventName", "TransferDomainToAnotherAwsAccount") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/privilege_escalation_root_login_without_mfa.yml b/rules/cloud/aws/privilege_escalation_root_login_without_mfa.yml index bf0cc17dd..9366d264b 100644 --- a/rules/cloud/aws/privilege_escalation_root_login_without_mfa.yml +++ b/rules/cloud/aws/privilege_escalation_root_login_without_mfa.yml @@ -1,25 +1,30 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS Root Login Without MFA" +- aws +name: AWS Root Login Without MFA impact: confidentiality: 3 integrity: 2 availability: 2 -category: "Initial Access" -technique: "T1078 - Valid Accounts" +category: Initial Access +technique: T1078 - Valid Accounts adversary: origin -description: "Identifies attempts to login to AWS as the root user without using multi-factor authentication (MFA). Amazon AWS best practices indicate that the root user should be protected by MFA" +description: Identifies attempts to login to AWS as the root user without using multi-factor authentication (MFA). Amazon + AWS best practices indicate that the root user should be protected by MFA references: - - "https://attack.mitre.org/tactics/TA0004/" - - "https://attack.mitre.org/techniques/T1078/" - - "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html" +- https://attack.mitre.org/tactics/TA0004/ +- https://attack.mitre.org/techniques/T1078/ +- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html where: | - equals("log.eventSource", "signin.amazonaws.com") && + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "signin.amazonaws.com") && equals("log.eventName", "ConsoleLogin") && - equals("log.userIdentityType", "root") && - equals("log.additionalEventData.MFAUsed", "no") + equals("log.userIdentity.type", "Root") && + equals("log.additionalEventData.MFAUsed", "No") && + equals("actionResult","success")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/privilege_escalation_updateassumerolepolicy.yml b/rules/cloud/aws/privilege_escalation_updateassumerolepolicy.yml index a6441ead5..5e2d08ed8 100644 --- a/rules/cloud/aws/privilege_escalation_updateassumerolepolicy.yml +++ b/rules/cloud/aws/privilege_escalation_updateassumerolepolicy.yml @@ -1,23 +1,27 @@ -# Rule version v1.0.0 - +# Rule contract revision 2026-09-17; SDK v1.1.31. dataTypes: - - aws -name: "AWS IAM Assume Role Policy Update" +- aws +name: AWS IAM Assume Role Policy Update impact: confidentiality: 2 integrity: 2 availability: 1 -category: "Initial Access" -technique: "T1078 - Valid Accounts" +category: Initial Access +technique: T1078 - Valid Accounts adversary: origin -description: "Identifies attempts to modify an AWS IAM Assume Role Policy. An adversary may attempt to modify the AssumeRolePolicy of a misconfigured role in order to gain the privileges of that role" +description: Identifies attempts to modify an AWS IAM Assume Role Policy. An adversary may attempt to modify the AssumeRolePolicy + of a misconfigured role in order to gain the privileges of that role references: - - "https://attack.mitre.org/tactics/TA0004/" - - "https://attack.mitre.org/techniques/T1078/" - - "https://labs.bishopfox.com/tech-blog/5-privesc-attack-vectors-in-aws" +- https://attack.mitre.org/tactics/TA0004/ +- https://attack.mitre.org/techniques/T1078/ +- https://labs.bishopfox.com/tech-blog/5-privesc-attack-vectors-in-aws where: | - equals("log.eventSource", "iam.amazonaws.com") && - equals("log.eventName", "UpdateAssumeRolePolicy") + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "iam.amazonaws.com") && + equals("log.eventName", "UpdateAssumeRolePolicy")) groupBy: - - adversary.ip - - adversary.user +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user From a388af649faa309a2c76f7a0f557124a9d2267d8 Mon Sep 17 00:00:00 2001 From: Ricardo Valdes Date: Thu, 17 Sep 2026 20:11:24 -0400 Subject: [PATCH 3/3] Accept both preserved and sanitized nested S3 headers --- filters/audits/aws.md | 6 +- filters/aws/aws.yml | 5 ++ plugins/alerts/aws_contract_test.go | 56 ++++++++++++++++++- .../aws/aws/s3_bucket_public_exposure.yml | 3 +- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/filters/audits/aws.md b/filters/audits/aws.md index c698fde71..1ba082c4d 100644 --- a/filters/audits/aws.md +++ b/filters/audits/aws.md @@ -52,7 +52,9 @@ still be checked against actual customer records when AWS telemetry is available queries. SDK `contains` only handles string fields. Covered consumers include startup data, RDS public restore, S3 versioning/ACL/block controls, snapshot sharing, EC2 metadata, ECS task definitions and security-group permissions. - SDK field sanitization means `x-amz-acl` is read as `xamzacl` after JSON parsing. + The SDK sanitization helper strips punctuation, but the closed JSON step + may retain punctuation in nested keys. S3 header aliases accept both `x-amz-acl` + and `xamzacl`; a compatibility test exercises both extraction layouts. - Restrict exposure predicates to permission additions or relaxed controls; removing snapshot or security-group permissions does not establish exposure. Correct cross-account ARN comparison to exact account boundaries and quote the @@ -113,7 +115,7 @@ claim to have run live grouping or alert creation. assert exact query terms, account/collector/actor/IP scopes, threshold/window boundaries, missing placeholders, unrelated populations and sequence/OR behavior. - Strict SDK YAML decoding, field-name/type checks, final Event serialization and - the shared contract suite pass: **275 pass records, zero skips/failures** in the + the shared contract suite pass: **278 pass records, zero skips/failures** in the source run with official examples supplied. The history subprocess asserts its 22 cases internally; the outer pass count is not a count of live detections. diff --git a/filters/aws/aws.yml b/filters/aws/aws.yml index 8608ab49d..44a4f8128 100644 --- a/filters/aws/aws.yml +++ b/filters/aws/aws.yml @@ -132,6 +132,7 @@ pipeline: - rename: from: - log.additionalEventData.xamzid2 + - log.additionalEventData.x-amz-id-2 to: log.additionalEventDataXamzId2 - rename: from: @@ -144,18 +145,22 @@ pipeline: - rename: from: - log.requestParameters.xamzacl + - log.requestParameters.x-amz-acl to: log.requestParametersXAmzAcl - rename: from: - log.requestParameters.xamzserversideencryption + - log.requestParameters.x-amz-server-side-encryption to: log.requestParametersXAmzServerSideEncryption - rename: from: - log.responseElements.xamzexpiration + - log.responseElements.x-amz-expiration to: log.responseElementsXAmzExpiration - rename: from: - log.responseElements.xamzserversideencryption + - log.responseElements.x-amz-server-side-encryption to: log.responseElementsXAmzServerSideEncryption - rename: from: diff --git a/plugins/alerts/aws_contract_test.go b/plugins/alerts/aws_contract_test.go index 60c9de5b3..bc7d27847 100644 --- a/plugins/alerts/aws_contract_test.go +++ b/plugins/alerts/aws_contract_test.go @@ -107,6 +107,11 @@ func awsRegex(t *testing.T, g *plugins.Grok, cfg *plugins.Config) *regexp.Regexp return r } func awsParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache, enrichment ...map[string]any) string { + return awsParseMode(t, cfg, raw, dataSource, cache, false, enrichment...) +} + +// Both modes model the unresolved nested-key behavior of the closed JSON step. +func awsParseMode(t *testing.T, cfg *plugins.Config, raw string, dataSource string, cache *plugins.CELCache, preserveNested bool, enrichment ...map[string]any) string { t.Helper() draft := map[string]any{"raw": raw, "dataType": "aws", "dataSource": dataSource, "log": map[string]any{}} for _, stage := range cfg.Pipeline { @@ -219,7 +224,15 @@ func awsParse(t *testing.T, cfg *plugins.Config, raw string, dataSource string, if e := json.Unmarshal([]byte(str), &parsed); e != nil { t.Fatal(e) } - for key, value := range awsSanitizeJSON(parsed) { + normalized := awsSanitizeJSON(parsed) + if preserveNested { + normalized = map[string]any{} + for key, value := range parsed { + utils.SanitizeField(&key) + normalized[key] = value + } + } + for key, value := range normalized { awsPut(draft, "log."+key, value, false) } case "cast": @@ -488,3 +501,44 @@ func TestAWSOfficialExamples(t *testing.T) { } t.Logf("modeled %d official examples; no live AWS telemetry or alerts", len(docs)) } + +// Stored Azure Event Grid records retain punctuation in nested claim keys. That is +// not proof of the AWS parser's behavior, so header consumers accept both layouts. +func TestAWSNestedKeyCompatibility(t *testing.T) { + cfg, rules, cache := awsConfig(t), awsRules(t), plugins.NewCELCache("aws-nested-keys") + raw := `{"eventVersion":"1.09","eventTime":"2026-09-17T12:00:00Z","eventType":"AwsApiCall","eventName":"PutBucketAcl","eventSource":"s3.amazonaws.com","requestParameters":{"x-amz-acl":"public-read","x-amz-server-side-encryption":"AES256"},"responseElements":{"x-amz-expiration":"expiry-test","x-amz-server-side-encryption":"AES256"},"additionalEventData":{"x-amz-id-2":"request-test"}}` + for _, preserved := range []bool{false, true} { + t.Run(fmt.Sprint(preserved), func(t *testing.T) { + out := awsParseMode(t, cfg, raw, "collector-test", cache, preserved) + expected := map[string]string{"log.requestParametersXAmzAcl": "public-read", "log.requestParametersXAmzServerSideEncryption": "AES256", "log.responseElementsXAmzExpiration": "expiry-test", "log.responseElementsXAmzServerSideEncryption": "AES256", "log.additionalEventDataXamzId2": "request-test"} + for field, want := range expected { + if got := gjson.Get(out, field).String(); got != want { + t.Errorf("%s=%q want %q", field, got, want) + } + } + yes, e := cache.Eval(rules["s3_bucket_public_exposure"].Where, out) + if e != nil || !yes { + t.Fatalf("public ACL lost: %v %v", yes, e) + } + key := "log.requestParameters.xamzacl" + if preserved { + key = "log.requestParameters.x-amz-acl" + } + if gjson.Get(out, key).String() != "public-read" { + t.Error("original header lost") + } + var private map[string]any + if err := json.Unmarshal([]byte(raw), &private); err != nil { + t.Fatal(err) + } + private["requestParameters"].(map[string]any)["x-amz-acl"] = "private" + private["requestParametersXAmzAcl"] = "public-read" + encoded, _ := json.Marshal(private) + negative := awsParseMode(t, cfg, string(encoded), "collector-test", cache, preserved) + if yes, e := cache.Eval(rules["s3_bucket_public_exposure"].Where, negative); e != nil || yes { + t.Fatalf("flat input alias fabricates ACL exposure: %v %v", yes, e) + } + + }) + } +} diff --git a/rules/cloud/aws/aws/s3_bucket_public_exposure.yml b/rules/cloud/aws/aws/s3_bucket_public_exposure.yml index 9e033c562..9e1d7de9b 100644 --- a/rules/cloud/aws/aws/s3_bucket_public_exposure.yml +++ b/rules/cloud/aws/aws/s3_bucket_public_exposure.yml @@ -19,7 +19,8 @@ where: | (equals("actionResult","success") && (equals("log.eventSource","s3.amazonaws.com") && oneOf("log.eventName",["PutBucketAcl", "PutObjectAcl"]) && - (oneOf("log.requestParameters.xamzacl",["public-read", "public-read-write"]) || + ((oneOf("log.requestParameters.xamzacl",["public-read", "public-read-write"]) || + oneOf("log.requestParameters.x-amz-acl",["public-read", "public-read-write"])) || exists('log.requestParameters.AccessControlPolicy.AccessControlList.Grant.#(Grantee.URI=="http://acs.amazonaws.com/groups/global/AllUsers")')) || equals("log.eventSource","s3.amazonaws.com") && oneOf("log.eventName",["DeleteBucketPublicAccessBlock"]) ||