From 8f2e393c1f7549bb23d14b663ba5b0fbb1f90e24 Mon Sep 17 00:00:00 2001 From: merquiag Date: Fri, 2 Oct 2026 18:11:03 +0200 Subject: [PATCH 01/11] Add AWS EC2 Instance Connect key upload detection --- ...lic_keys_uploaded_by_a_single_identity.yml | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml diff --git a/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml new file mode 100644 index 0000000000..6823486b32 --- /dev/null +++ b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml @@ -0,0 +1,81 @@ +name: AWS EC2 Multiple SSH Public Keys Uploaded by a Single Identity +id: 62afb4a9-d9d0-464a-9326-aacf61c5a9f2 +version: 1 +creation_date: '2026-09-29' +modification_date: '2026-09-29' +author: Maria Jose Erquiaga, Splunk +status: production +type: TTP +description: >- + Detects an AWS identity making 10 or more successful EC2 Instance Connect + SendSSHPublicKey API calls within the configured search interval. EC2 + Instance Connect temporarily makes an SSH public key available to an + operating-system user on an EC2 instance. A burst of these calls may + indicate automated attempts to establish access or move laterally using + compromised AWS credentials. +data_source: + - AWS CloudTrail +search: |- + `cloudtrail` eventSource="ec2-instance-connect.amazonaws.com" eventName="SendSSHPublicKey" responseElements.success=true + | eval user=coalesce(user_name, 'userIdentity.arn', 'userIdentity.principalId') + | stats count as event_count + dc('requestParameters.instanceId') as unique_instances + values('requestParameters.instanceId') as targeted_instances + values('requestParameters.instanceOSUser') as targeted_users + values(src) as src + values(user_agent) as user_agent + values(vendor_region) as vendor_region + earliest(_time) as firstTime + latest(_time) as lastTime + by user + | where event_count >= 10 + | eval duration_seconds=lastTime-firstTime + | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` + | `aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity_filter` +how_to_implement: >- + Ingest AWS CloudTrail management events into Splunk using the + aws:cloudtrail sourcetype. Ensure that successful EC2 Instance Connect + SendSSHPublicKey events are collected. Schedule the analytic with a short + lookback window appropriate for the environment, such as 10 minutes, and + run it every 5 minutes. The search counts distinct CloudTrail eventID values + so overlapping CloudTrail trails or duplicate collection paths do not + inflate the threshold. +known_false_positives: >- + Administrators and automated orchestration may legitimately use EC2 + Instance Connect repeatedly during fleet administration, troubleshooting, + or ephemeral-access workflows. Review the AWS identity, source addresses, + user agent, target instances, target operating-system users, and whether + the activity corresponds to an approved administrative process. +references: + - https://docs.aws.amazon.com/ec2-instance-connect/latest/APIReference/API_SendSSHPublicKey.html + - https://stratus-red-team.cloud/attack-techniques/AWS/aws.lateral-movement.ec2-instance-connect/ + - https://www.elastic.co/guide/en/security/8.19/prebuilt-rule-8-19-14-aws-ec2-instance-connect-ssh-public-key-uploaded.html +finding: + title: AWS identity $user$ uploaded $event_count$ SSH public keys using EC2 Instance Connect + entity: + field: user + type: user + score: 50 +threat_objects: + - field: src + type: ip_address +analytic_story: + - Suspicious Cloud Instance Activities +asset_type: AWS Account +mitre_attack_id: + - T1021.004 + - T1021.008 +product: + - Splunk Cloud + - Splunk Enterprise + - Splunk Enterprise Security +category: cloud +security_domain: threat +tests: + - name: True Positive Test + attack_data: + - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1021.008/aws_ec2_multiple_ssh_public_keys_uploaded/aws_ec2_multiple_ssh_public_keys_uploaded.json + sourcetype: aws:cloudtrail + source: aws_cloudtrail + test_type: unit From 9e04e6de5dcea2bf68ffdc559c3c9e42d8f0380c Mon Sep 17 00:00:00 2001 From: themaryjo Date: Fri, 2 Oct 2026 18:46:49 +0200 Subject: [PATCH 02/11] Update aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml updating "how to implement" section --- ...ple_ssh_public_keys_uploaded_by_a_single_identity.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml index 6823486b32..876a222559 100644 --- a/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml +++ b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml @@ -33,14 +33,7 @@ search: |- | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity_filter` -how_to_implement: >- - Ingest AWS CloudTrail management events into Splunk using the - aws:cloudtrail sourcetype. Ensure that successful EC2 Instance Connect - SendSSHPublicKey events are collected. Schedule the analytic with a short - lookback window appropriate for the environment, such as 10 minutes, and - run it every 5 minutes. The search counts distinct CloudTrail eventID values - so overlapping CloudTrail trails or duplicate collection paths do not - inflate the threshold. +how_to_implement: You must install Splunk AWS Add on and enable CloudTrail logs in your AWS Environment. known_false_positives: >- Administrators and automated orchestration may legitimately use EC2 Instance Connect repeatedly during fleet administration, troubleshooting, From 51b2c9c28ca837ca7869bdc4a9ab825c8c7085aa Mon Sep 17 00:00:00 2001 From: themaryjo Date: Tue, 6 Oct 2026 15:15:45 +0200 Subject: [PATCH 03/11] Update aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml Group EC2 key uploads by AWS identity and count unique API events --- ...ultiple_ssh_public_keys_uploaded_by_a_single_identity.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml index 876a222559..69a28ec5d0 100644 --- a/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml +++ b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml @@ -17,8 +17,7 @@ data_source: - AWS CloudTrail search: |- `cloudtrail` eventSource="ec2-instance-connect.amazonaws.com" eventName="SendSSHPublicKey" responseElements.success=true - | eval user=coalesce(user_name, 'userIdentity.arn', 'userIdentity.principalId') - | stats count as event_count + | stats dc(eventID) as event_count dc('requestParameters.instanceId') as unique_instances values('requestParameters.instanceId') as targeted_instances values('requestParameters.instanceOSUser') as targeted_users @@ -27,7 +26,7 @@ search: |- values(vendor_region) as vendor_region earliest(_time) as firstTime latest(_time) as lastTime - by user + by user_arn | where event_count >= 10 | eval duration_seconds=lastTime-firstTime | `security_content_ctime(firstTime)` From 506d226c454d22913292ea9d9a1ca97abb887722 Mon Sep 17 00:00:00 2001 From: merquiag Date: Tue, 13 Oct 2026 15:36:38 +0200 Subject: [PATCH 04/11] Add AWS IAM role external trust detection and Startup script modified --- ...s_iam_role_created_with_external_trust.yml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 detections/cloud/aws_iam_role_created_with_external_trust.yml diff --git a/detections/cloud/aws_iam_role_created_with_external_trust.yml b/detections/cloud/aws_iam_role_created_with_external_trust.yml new file mode 100644 index 0000000000..198a910144 --- /dev/null +++ b/detections/cloud/aws_iam_role_created_with_external_trust.yml @@ -0,0 +1,96 @@ +name: AWS IAM Role Created with External Trust +id: 6b7f646b-5ddc-48c9-baa3-fd66ac202c18 +version: 1 +creation_date: '2026-10-13' +modification_date: '2026-10-13' +author: Maria Jose Erquiaga, Splunk +status: experimental +type: TTP +description: >- + Detects a successful AWS IAM CreateRole event whose trust policy allows + sts:AssumeRole by a principal in another AWS account or by a wildcard + principal. This configuration may provide persistent access if the + principal can assume the role. The event alone does not establish that + the role was assumed or that its creation was unauthorized. +data_source: + - AWS CloudTrail +search: |- + `cloudtrail` eventSource="iam.amazonaws.com" eventName="CreateRole" + | where isnull(errorCode) OR errorCode="" OR lower(errorCode)="success" + | eval policy_raw='requestParameters.assumeRolePolicyDocument', + role_name='requestParameters.roleName', + role_arn='responseElements.role.arn', + user_arn=coalesce(user_arn, 'userIdentity.arn'), + src=coalesce(src, sourceIPAddress) + | where isnotnull(policy_raw) + | eval policy=if(match(policy_raw, "(?i)^%7b"), urldecode(policy_raw), policy_raw) + | rex field=role_arn "^arn:[^:]+:iam::(?[0-9]{12}):role/" + | eval role_account_id=coalesce(account_from_role_arn, recipientAccountId) + | eval statements_json=json_extract(policy, "Statement") + | eval statements=if(match(statements_json, "^\s*\["), json_array_to_mv(statements_json), statements_json) + | mvexpand statements + | eval effect=json_extract(statements, "Effect"), + actions_json=json_extract(statements, "Action"), + principal_json=json_extract(statements, "Principal") + | eval actions=if(match(actions_json, "^\s*\["), json_array_to_mv(actions_json, true()), actions_json) + | mvexpand actions + | where effect="Allow" AND actions="sts:AssumeRole" + | eval aws_json=json_extract(principal_json, "AWS") + | eval principals=case( + match(aws_json, "^\s*\["), json_array_to_mv(aws_json, true()), + isnotnull(aws_json), aws_json, + principal_json="*", "*" + ) + | mvexpand principals + | eval trust_principal=principals + | rex field=trust_principal "^arn:[^:]+:iam::(?[0-9]{12}):" + | eval principal_account_id=if( + match(trust_principal, "^[0-9]{12}$"), + trust_principal, + principal_account_from_arn + ) + | eval trust_type=case( + trust_principal="*", "wildcard", + isnotnull(role_account_id) AND isnotnull(principal_account_id) + AND principal_account_id!=role_account_id, "external" + ) + | where trust_type="wildcard" OR trust_type="external" + | eval event_key=coalesce(eventID, md5(_raw)) + | dedup event_key trust_principal + | table _time eventID user_arn src role_name role_arn role_account_id trust_type trust_principal principal_account_id + | `aws_iam_role_created_with_external_trust_filter` +how_to_implement: You must install Splunk AWS Add on and enable CloudTrail logs in your AWS Environment. +known_false_positives: >- + Approved cross-account integrations and deliberately configured wildcard + trust may match. Review the role owner, trust-policy conditions, role + permissions, and whether the principal is expected. +references: + - https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateRole.html + - https://stratus-red-team.cloud/attack-techniques/aws/aws.persistence.iam-backdoor-role/ +finding: + title: AWS IAM role $role_name$ created with $trust_type$ trust + entity: + field: user_arn + type: user + score: 40 +threat_objects: + - field: src + type: ip_address +analytic_story: + - AWS IAM Privilege Escalation +asset_type: AWS Account +mitre_attack_id: + - T1098.003 +product: + - Splunk Cloud + - Splunk Enterprise + - Splunk Enterprise Security +category: cloud +security_domain: threat +tests: + - name: True Positive Test + attack_data: + - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098.003/aws_iam_role_created_with_external_trust/aws_iam_role_created_with_external_trust.json + sourcetype: aws:cloudtrail + source: aws_cloudtrail + test_type: unit From 90e414dfffcbd043f9581c73a6131a951ee01bbc Mon Sep 17 00:00:00 2001 From: merquiag Date: Tue, 13 Oct 2026 16:41:34 +0200 Subject: [PATCH 05/11] Add AWS EC2 startup script modification detection --- ...up_script_modified_on_stopped_instance.yml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml diff --git a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml new file mode 100644 index 0000000000..72e9b8c33f --- /dev/null +++ b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml @@ -0,0 +1,73 @@ +name: AWS EC2 Startup Script Modified on Stopped Instance +id: 84578867-02bf-475e-9815-6e8e0d798253 +version: 1 +creation_date: '2026-10-07' +modification_date: '2026-10-07' +author: Maria Jose Erquiaga +status: experimental +type: TTP +description: >- + A successful EC2 StopInstances request was followed within one hour by a + successful ModifyInstanceAttribute request targeting userData on the same + instance, by the same IAM principal. This may indicate an attempt to change + instance startup behavior or establish persistence. These CloudTrail events + do not confirm that the modified user-data script executed. +search: |- + `cloudtrail` eventSource="ec2.amazonaws.com" eventName IN ("StopInstances", "ModifyInstanceAttribute") + | eval actor='userIdentity.arn' + | eval instance_id=case( + eventName="StopInstances", 'requestParameters.instancesSet.items{}.instanceId', + eventName="ModifyInstanceAttribute", 'requestParameters.instanceId' + ) + | mvexpand instance_id + | where isnotnull(eventID) AND isnotnull(instance_id) AND isnotnull(actor) + AND (isnull(errorCode) OR errorCode="" OR lower(errorCode)="success") + | where eventName="StopInstances" + OR (eventName="ModifyInstanceAttribute" AND isnotnull('requestParameters.userData')) + | dedup eventID instance_id + | eval event_epoch=coalesce(strptime(eventTime, "%Y-%m-%dT%H:%M:%SZ"), _time) + | sort 0 actor instance_id awsRegion event_epoch + | streamstats current=f + last(eval(if(eventName="StopInstances", event_epoch, null()))) AS stop_epoch + last(eval(if(eventName="StopInstances", eventID, null()))) AS stop_event_id + BY actor instance_id awsRegion + | where eventName="ModifyInstanceAttribute" AND isnotnull(stop_epoch) + | eval elapsed_seconds=event_epoch-stop_epoch + | where elapsed_seconds>0 AND elapsed_seconds<=3600 + | eval stop_time_utc=strftime(stop_epoch, "%Y-%m-%dT%H:%M:%SZ") + | eval modification_time_utc=strftime(event_epoch, "%Y-%m-%dT%H:%M:%SZ") + | rename eventID AS modification_event_id + | table actor instance_id awsRegion stop_time_utc modification_time_utc elapsed_seconds stop_event_id modification_event_id + | `aws_ec2_startup_script_modified_on_stopped_instance_filter` +how_to_implement: You must install Splunk AWS Add on and enable CloudTrail logs in your AWS Environment. +known_false_positives: >- + Authorized administrators and infrastructure automation may stop instances + and update their user data. Review the IAM principal, instance, change + window, and approved automation before escalating. +references: + - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html + - https://stratus-red-team.cloud/attack-techniques/AWS/aws.execution.ec2-user-data/ +finding: + title: IAM principal $actor$ modified user data on stopped EC2 instance $instance_id$ + entity: + field: actor + type: user + score: 50 +analytic_story: + - Suspicious Cloud User Activities +asset_type: AWS Account +mitre_attack_id: + - T1578.005 +product: + - Splunk Enterprise + - Splunk Enterprise Security + - Splunk Cloud +category: cloud +security_domain: threat +tests: + - name: True Positive Test + attack_data: + - data: https://github.com/splunk/attack_data/blob/master/datasets/attack_techniques/T1578.005/aws_ec2_startup_script_modification/aws_ec2_startup_script_modification.json + sourcetype: aws:cloudtrail + source: aws_cloudtrail + test_type: unit From 2f7d2761087a9935c2f3bf46aa4b8978fa1e6e63 Mon Sep 17 00:00:00 2001 From: merquiag Date: Tue, 13 Oct 2026 16:57:27 +0200 Subject: [PATCH 06/11] Add CloudTrail data source to EC2 startup script detection --- .../aws_ec2_startup_script_modified_on_stopped_instance.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml index 72e9b8c33f..ea72a931bf 100644 --- a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml +++ b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml @@ -12,6 +12,8 @@ description: >- instance, by the same IAM principal. This may indicate an attempt to change instance startup behavior or establish persistence. These CloudTrail events do not confirm that the modified user-data script executed. +data_source: + - AWS CloudTrail search: |- `cloudtrail` eventSource="ec2.amazonaws.com" eventName IN ("StopInstances", "ModifyInstanceAttribute") | eval actor='userIdentity.arn' From 3e2b1eedacfaa1cdd48e31a03a6655a8767cafde Mon Sep 17 00:00:00 2001 From: merquiag Date: Wed, 14 Oct 2026 15:07:31 +0200 Subject: [PATCH 07/11] Enable EC2 startup script and IAM trust detections for testing --- .../aws_ec2_startup_script_modified_on_stopped_instance.yml | 4 ++-- detections/cloud/aws_iam_role_created_with_external_trust.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml index ea72a931bf..51c84185b0 100644 --- a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml +++ b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml @@ -4,7 +4,7 @@ version: 1 creation_date: '2026-10-07' modification_date: '2026-10-07' author: Maria Jose Erquiaga -status: experimental +status: production type: TTP description: >- A successful EC2 StopInstances request was followed within one hour by a @@ -69,7 +69,7 @@ security_domain: threat tests: - name: True Positive Test attack_data: - - data: https://github.com/splunk/attack_data/blob/master/datasets/attack_techniques/T1578.005/aws_ec2_startup_script_modification/aws_ec2_startup_script_modification.json + - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1578.005/aws_ec2_startup_script_modification/aws_ec2_startup_script_modification.json sourcetype: aws:cloudtrail source: aws_cloudtrail test_type: unit diff --git a/detections/cloud/aws_iam_role_created_with_external_trust.yml b/detections/cloud/aws_iam_role_created_with_external_trust.yml index 198a910144..5dfa1e7595 100644 --- a/detections/cloud/aws_iam_role_created_with_external_trust.yml +++ b/detections/cloud/aws_iam_role_created_with_external_trust.yml @@ -4,7 +4,7 @@ version: 1 creation_date: '2026-10-13' modification_date: '2026-10-13' author: Maria Jose Erquiaga, Splunk -status: experimental +status: production type: TTP description: >- Detects a successful AWS IAM CreateRole event whose trust policy allows From 1bf2a1028f1967b4bff0176c8a7381f5f57ba54b Mon Sep 17 00:00:00 2001 From: themaryjo Date: Thu, 15 Oct 2026 12:36:00 +0200 Subject: [PATCH 08/11] Update aws_ec2_startup_script_modified_on_stopped_instance.yml updating filter --- .../aws_ec2_startup_script_modified_on_stopped_instance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml index 51c84185b0..0505363b99 100644 --- a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml +++ b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml @@ -25,7 +25,7 @@ search: |- | where isnotnull(eventID) AND isnotnull(instance_id) AND isnotnull(actor) AND (isnull(errorCode) OR errorCode="" OR lower(errorCode)="success") | where eventName="StopInstances" - OR (eventName="ModifyInstanceAttribute" AND isnotnull('requestParameters.userData')) + OR (eventName="ModifyInstanceAttribute" AND 'requestParameters.attribute'="userData") | dedup eventID instance_id | eval event_epoch=coalesce(strptime(eventTime, "%Y-%m-%dT%H:%M:%SZ"), _time) | sort 0 actor instance_id awsRegion event_epoch From 02496837b915bf83971ed3e4555f6b27570adadf Mon Sep 17 00:00:00 2001 From: themaryjo Date: Thu, 15 Oct 2026 17:44:35 +0200 Subject: [PATCH 09/11] Update aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml since we are using "user_arn" in the detection, we should also use it in finding --- ...multiple_ssh_public_keys_uploaded_by_a_single_identity.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml index 69a28ec5d0..158048b69e 100644 --- a/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml +++ b/detections/cloud/aws_ec2_multiple_ssh_public_keys_uploaded_by_a_single_identity.yml @@ -44,9 +44,9 @@ references: - https://stratus-red-team.cloud/attack-techniques/AWS/aws.lateral-movement.ec2-instance-connect/ - https://www.elastic.co/guide/en/security/8.19/prebuilt-rule-8-19-14-aws-ec2-instance-connect-ssh-public-key-uploaded.html finding: - title: AWS identity $user$ uploaded $event_count$ SSH public keys using EC2 Instance Connect + title: AWS identity $user_arn$ uploaded $event_count$ SSH public keys using EC2 Instance Connect entity: - field: user + field: user_arn type: user score: 50 threat_objects: From 9e547b43ee0230ce31039adf7b57ff0c6f8fca72 Mon Sep 17 00:00:00 2001 From: themaryjo Date: Thu, 15 Oct 2026 18:57:28 +0200 Subject: [PATCH 10/11] Update aws_iam_role_created_with_external_trust.yml Modified detection to handle wildcard IAM actions in external role trust detection --- .../cloud/aws_iam_role_created_with_external_trust.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/detections/cloud/aws_iam_role_created_with_external_trust.yml b/detections/cloud/aws_iam_role_created_with_external_trust.yml index 5dfa1e7595..c4ade2099d 100644 --- a/detections/cloud/aws_iam_role_created_with_external_trust.yml +++ b/detections/cloud/aws_iam_role_created_with_external_trust.yml @@ -34,7 +34,10 @@ search: |- principal_json=json_extract(statements, "Principal") | eval actions=if(match(actions_json, "^\s*\["), json_array_to_mv(actions_json, true()), actions_json) | mvexpand actions - | where effect="Allow" AND actions="sts:AssumeRole" + | mvexpand actions + | eval trust_action=actions + | eval action_pattern=replace(replace(lower(trust_action), "[*]", "%"), "[?]", "_") + | where effect="Allow" AND like("sts:assumerole", action_pattern) | eval aws_json=json_extract(principal_json, "AWS") | eval principals=case( match(aws_json, "^\s*\["), json_array_to_mv(aws_json, true()), @@ -57,7 +60,7 @@ search: |- | where trust_type="wildcard" OR trust_type="external" | eval event_key=coalesce(eventID, md5(_raw)) | dedup event_key trust_principal - | table _time eventID user_arn src role_name role_arn role_account_id trust_type trust_principal principal_account_id + | table _time eventID user_arn src role_name role_arn role_account_id trust_type trust_principal principal_account_id trust_action | `aws_iam_role_created_with_external_trust_filter` how_to_implement: You must install Splunk AWS Add on and enable CloudTrail logs in your AWS Environment. known_false_positives: >- From 235f8656df8c207ea3497d24740773a1e515476c Mon Sep 17 00:00:00 2001 From: themaryjo Date: Fri, 16 Oct 2026 11:10:59 +0200 Subject: [PATCH 11/11] Update aws_ec2_startup_script_modified_on_stopped_instance.yml replaced "actor" by "user_arn" for userIdentity.arn field --- ...startup_script_modified_on_stopped_instance.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml index 0505363b99..b93074de64 100644 --- a/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml +++ b/detections/cloud/aws_ec2_startup_script_modified_on_stopped_instance.yml @@ -16,30 +16,30 @@ data_source: - AWS CloudTrail search: |- `cloudtrail` eventSource="ec2.amazonaws.com" eventName IN ("StopInstances", "ModifyInstanceAttribute") - | eval actor='userIdentity.arn' + | eval user_arn='userIdentity.arn' | eval instance_id=case( eventName="StopInstances", 'requestParameters.instancesSet.items{}.instanceId', eventName="ModifyInstanceAttribute", 'requestParameters.instanceId' ) | mvexpand instance_id - | where isnotnull(eventID) AND isnotnull(instance_id) AND isnotnull(actor) + | where isnotnull(eventID) AND isnotnull(instance_id) AND isnotnull(user_arn) AND (isnull(errorCode) OR errorCode="" OR lower(errorCode)="success") | where eventName="StopInstances" OR (eventName="ModifyInstanceAttribute" AND 'requestParameters.attribute'="userData") | dedup eventID instance_id | eval event_epoch=coalesce(strptime(eventTime, "%Y-%m-%dT%H:%M:%SZ"), _time) - | sort 0 actor instance_id awsRegion event_epoch + | sort 0 user_arn instance_id awsRegion event_epoch | streamstats current=f last(eval(if(eventName="StopInstances", event_epoch, null()))) AS stop_epoch last(eval(if(eventName="StopInstances", eventID, null()))) AS stop_event_id - BY actor instance_id awsRegion + BY user_arn instance_id awsRegion | where eventName="ModifyInstanceAttribute" AND isnotnull(stop_epoch) | eval elapsed_seconds=event_epoch-stop_epoch | where elapsed_seconds>0 AND elapsed_seconds<=3600 | eval stop_time_utc=strftime(stop_epoch, "%Y-%m-%dT%H:%M:%SZ") | eval modification_time_utc=strftime(event_epoch, "%Y-%m-%dT%H:%M:%SZ") | rename eventID AS modification_event_id - | table actor instance_id awsRegion stop_time_utc modification_time_utc elapsed_seconds stop_event_id modification_event_id + | table user_arn instance_id awsRegion stop_time_utc modification_time_utc elapsed_seconds stop_event_id modification_event_id | `aws_ec2_startup_script_modified_on_stopped_instance_filter` how_to_implement: You must install Splunk AWS Add on and enable CloudTrail logs in your AWS Environment. known_false_positives: >- @@ -50,9 +50,9 @@ references: - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html - https://stratus-red-team.cloud/attack-techniques/AWS/aws.execution.ec2-user-data/ finding: - title: IAM principal $actor$ modified user data on stopped EC2 instance $instance_id$ + title: IAM principal $user_arn$ modified user data on stopped EC2 instance $instance_id$ entity: - field: actor + field: user_arn type: user score: 50 analytic_story: