Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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
| stats dc(eventID) 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_arn
| 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: 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,
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_arn$ uploaded $event_count$ SSH public keys using EC2 Instance Connect
entity:
field: user_arn
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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: production
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.
data_source:
- AWS CloudTrail
search: |-
`cloudtrail` eventSource="ec2.amazonaws.com" eventName IN ("StopInstances", "ModifyInstanceAttribute")
| 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(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 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 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 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: >-
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 $user_arn$ modified user data on stopped EC2 instance $instance_id$
entity:
field: user_arn
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://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
99 changes: 99 additions & 0 deletions detections/cloud/aws_iam_role_created_with_external_trust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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: production
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::(?<account_from_role_arn>[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
| 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()),
isnotnull(aws_json), aws_json,
principal_json="*", "*"
)
| mvexpand principals
| eval trust_principal=principals
| rex field=trust_principal "^arn:[^:]+:iam::(?<principal_account_from_arn>[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 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: >-
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
Loading