Describe the bug
AWS CreateAccessKey (detections/cloud/aws_createaccesskey.yml, id 2a9b80d3-6340-4345-11ad-212bf3d0d111) is meant to surface a user creating an access key for a different user:
| eval match=if(match(userIdentity.userName,requestParameters.userName),1,0)
| search match=0
Inside eval, the dotted field names are not single-quoted, so SPL parses userIdentity.userName as the . concatenation of the (non-existent) fields userIdentity and userName. Both arguments to match() evaluate to null, match is always 0, and search match=0 keeps every event. As shipped, the filter does nothing: the rule fires on every successful non-console CreateAccessKey, including the routine case of a user rotating their own key.
Repro
Self-contained, with no data needed:
| makeresults count=2
| streamstats count as n
| eval _raw=if(n==1, "{\"userIdentity\":{\"userName\":\"alice\"},\"requestParameters\":{\"userName\":\"alice\"}}", "{\"userIdentity\":{\"userName\":\"alice\"},\"requestParameters\":{\"userName\":\"bob\"}}")
| spath
| eval case=if(n==1,"self: alice creates a key for alice","cross-user: alice creates a key for bob")
| eval match=if(match(userIdentity.userName,requestParameters.userName),1,0)
| eval match_quoted=if('userIdentity.userName'=='requestParameters.userName',1,0)
| table case userIdentity.userName requestParameters.userName match match_quoted
Output:
| case |
userIdentity.userName |
requestParameters.userName |
match (shipped) |
match_quoted |
| self: alice creates a key for alice |
alice |
alice |
0 |
1 |
| cross-user: alice creates a key for bob |
alice |
bob |
0 |
0 |
The self-key row should be excluded (match=1), but the shipped expression returns 0.
Expected behavior
Only cross-user key creation should produce a result. A minimal fix quotes the fields and compares them directly. match() also treats its second argument as a regex, so a username like svc. would match svcX:
| eval match=if('userIdentity.userName'=='requestParameters.userName',1,0)
| search match=0
One related case to consider: when requestParameters.userName is absent (the key is for the caller), the request is a self-key and should also be excluded, e.g. if(isnull('requestParameters.userName') OR 'userIdentity.userName'=='requestParameters.userName',1,0).
Additional context: the console exclusion no longer matches console calls
The base search excludes console activity with userAgent!=console.amazonaws.com. A real console-issued CreateAccessKey captured from an AWS account in 2026 records a browser user agent (Mozilla/5.0 ... Chrome/...) together with "sessionCredentialFromConsole": "true", not console.amazonaws.com. Keys created in the console today therefore aren't excluded. A scripted caller can also evade the rule by sending the literal console.amazonaws.com user agent.
sessionCredentialFromConsole is not a clean replacement key. It marks console-session credentials, not the client, and AWS's own log examples show CloudShell CLI calls (exec-env/CloudShell) carrying it, including IAM calls. Excluding on it would hide cross-user key creation scripted from CloudShell. Once the cross-user filter works, dropping the console exclusion altogether and suppressing known provisioning principals in aws_createaccesskey_filter may be the better trade-off.
App Version:
- ESCU: reproduced on 5.26.0; SPL unchanged on
develop (64acde7, rule version 11)
- Splunk Enterprise 9.3.10, Splunk_TA_aws 7.11.0
Describe the bug
AWS CreateAccessKey(detections/cloud/aws_createaccesskey.yml, id2a9b80d3-6340-4345-11ad-212bf3d0d111) is meant to surface a user creating an access key for a different user:Inside
eval, the dotted field names are not single-quoted, so SPL parsesuserIdentity.userNameas the.concatenation of the (non-existent) fieldsuserIdentityanduserName. Both arguments tomatch()evaluate to null,matchis always0, andsearch match=0keeps every event. As shipped, the filter does nothing: the rule fires on every successful non-consoleCreateAccessKey, including the routine case of a user rotating their own key.Repro
Self-contained, with no data needed:
Output:
The self-key row should be excluded (
match=1), but the shipped expression returns0.Expected behavior
Only cross-user key creation should produce a result. A minimal fix quotes the fields and compares them directly.
match()also treats its second argument as a regex, so a username likesvc.would matchsvcX:One related case to consider: when
requestParameters.userNameis absent (the key is for the caller), the request is a self-key and should also be excluded, e.g.if(isnull('requestParameters.userName') OR 'userIdentity.userName'=='requestParameters.userName',1,0).Additional context: the console exclusion no longer matches console calls
The base search excludes console activity with
userAgent!=console.amazonaws.com. A real console-issuedCreateAccessKeycaptured from an AWS account in 2026 records a browser user agent (Mozilla/5.0 ... Chrome/...) together with"sessionCredentialFromConsole": "true", notconsole.amazonaws.com. Keys created in the console today therefore aren't excluded. A scripted caller can also evade the rule by sending the literalconsole.amazonaws.comuser agent.sessionCredentialFromConsoleis not a clean replacement key. It marks console-session credentials, not the client, and AWS's own log examples show CloudShell CLI calls (exec-env/CloudShell) carrying it, including IAM calls. Excluding on it would hide cross-user key creation scripted from CloudShell. Once the cross-user filter works, dropping the console exclusion altogether and suppressing known provisioning principals inaws_createaccesskey_filtermay be the better trade-off.App Version:
develop(64acde7, rule version 11)