GetCallerIdentity in CloudTrail: The First Call Made With a Stolen Key
Published
A caller asking AWS STS which identity its credentials belong to; it needs no permissions, so it is often the first call made with a newly found access key.
AWS's description
Returns details about the IAM user or role whose credentials are used to call the operation.
Source: AWS API reference: GetCallerIdentity · copied from AWS's documentation and refreshed weekly
Fields that matter in an investigation
| Field | What it tells you |
|---|---|
| 1userIdentity.accessKeyId | The key that made the call. AKIA is a long-term key of an IAM user or the root user, ASIA temporary credentials from STS; a long-term key tested from a new address is the case to chase. |
| 2userIdentity.arn | The identity behind the key, which is also the answer the call returned. The record does not repeat the response. |
| 3userIdentity.type | IAMUser, AssumedRole, FederatedUser or Root, the kind of identity the credentials belong to. |
| 4sourceIPAddress | Where the call came from. Compare with the addresses this key normally uses; a key's first call from a hosting provider or VPN exit is typical of a leak being tested. |
| 5userAgent | The client. CLI and SDK user agents name the tool and version; one the key has never used before is worth a look. |
| 6responseElements | Null in the recorded events. The account, ARN and user ID the call returns are not logged, but they match userIdentity. |
| 7userIdentity.sessionContext.sessionIssuer.arn | For temporary credentials, the role or user that issued the session, which is the identity to investigate when the key is an ASIA key. |
| 8tlsDetails.clientProvidedHostHeader | The STS endpoint the client called, sts.amazonaws.com for the global endpoint or a Regional host name. |
Every field is checked on each build against AWS's CloudTrail record contents and userIdentity documentation and the example and recorded GetCallerIdentity records shown below.
What the record looks like
AWS does not publish an example GetCallerIdentity record; the record below was captured during an attack simulation.
Recorded during an attack simulation: Stratus Red Team technique aws.persistence.iam-create-user-login-profile
Numbered lines are the fields in the table.
A real CloudTrail record from a test detonation, published by Stratus Red Team (Datadog, Apache License 2.0) and anonymized there; account IDs, addresses, access key IDs and session tokens are replaced here with AWS's documentation values, and the operator's user name with "operator". The anonymizer also changes values such as the Region.
Recorded during an attack simulation: Stratus Red Team technique aws.persistence.sts-federation-token
Show the recorded GetCallerIdentity record
Numbered lines are the fields in the table.
A real CloudTrail record from a test detonation, published by Stratus Red Team (Datadog, Apache License 2.0) and anonymized there; account IDs, addresses, access key IDs and session tokens are replaced here with AWS's documentation values, and the operator's user name with "operator". The anonymizer also changes values such as the Region.
What it records
GetCallerIdentity returns the account ID, ARN and user ID of the identity whose credentials signed the request. CloudTrail logs it as a management event from sts.amazonaws.com with readOnly true in the recorded events, and both requestParameters and responseElements are null there: there is nothing to request, and the answer is not repeated because userIdentity already holds it.
AWS states that the call needs no permissions and still works when a policy explicitly denies sts:GetCallerIdentity, since the same information comes back when access is denied. Every valid credential can make the call, whatever its permissions, and nothing about it fails, which makes it the cheapest way to check that a key works.
When it matters
Because it always succeeds and changes nothing, GetCallerIdentity is the first call people make with credentials they have just found: to learn whose key it is and which account it opens (MITRE ATT&CK T1087.004). Leaked keys in code repositories, CI logs and public container images are tested this way, and an attacker who already holds a key runs it to confirm which identity a session acts as.
The same call is made all day by legitimate tooling, so a record is suspicious only in context:
- A long-term key (AKIA…) used from a new place. Its first call from an address, network or country it has never used, especially a hosting provider, is the classic sign of a leaked key being tested.
- A client the key has never used, such as a scripting SDK for a key that normally runs a single application.
- What follows. Discovery calls (
ListBuckets,ListUsers,Describe*across services) or persistence (CreateAccessKey,CreateUser,CreateLoginProfile) from the same key and address minutes later turn a test into an intrusion.
How to investigate
- Note
userIdentity.accessKeyId,userIdentity.arn,sourceIPAddressanduserAgent. - Compare the address and client with the key’s history. A key that has always called from your build servers and now calls from elsewhere is enough to act on.
- List every event with the same
userIdentity.accessKeyIdfrom the first unfamiliar call onwards. For an ASIA key, also follow the identity inuserIdentity.sessionContext.sessionIssuer.arnback to where the session came from. - If the activity is not explained, deactivate the key with
UpdateAccessKey, then look for persistence it created.
GetCallerIdentity calls in Microsoft Sentinel from a key and address pair not seen in the previous 30 days:
let Known = AWSCloudTrail
| where TimeGenerated between (ago(30d) .. ago(1d))
| distinct UserIdentityAccessKeyId, SourceIpAddress;
AWSCloudTrail
| where TimeGenerated > ago(1d)
| where EventName == "GetCallerIdentity"
| join kind=leftanti Known on UserIdentityAccessKeyId, SourceIpAddress
| project TimeGenerated, UserIdentityArn, UserIdentityType, UserIdentityAccessKeyId, SourceIpAddress, UserAgent
Everything a long-term key did in the hour after such a call:
let Tested = AWSCloudTrail
| where TimeGenerated > ago(1d)
| where EventName == "GetCallerIdentity" and UserIdentityAccessKeyId startswith "AKIA"
| summarize TestedAt = min(TimeGenerated) by UserIdentityAccessKeyId;
AWSCloudTrail
| where TimeGenerated > ago(1d)
| join kind=inner Tested on UserIdentityAccessKeyId
| where TimeGenerated between (TestedAt .. (TestedAt + 1h))
| summarize Calls = count(), Actions = make_set(EventName, 40), Addresses = make_set(SourceIpAddress, 10)
by UserIdentityAccessKeyId, UserIdentityArn
In Athena, over a table on the trail’s S3 bucket (named cloudtrail_logs here):
SELECT useridentity.accesskeyid, useridentity.arn, sourceipaddress, useragent,
min(eventtime) AS first_seen, count(*) AS calls
FROM cloudtrail_logs
WHERE eventname = 'GetCallerIdentity'
GROUP BY useridentity.accesskeyid, useridentity.arn, sourceipaddress, useragent
ORDER BY first_seen DESC
LIMIT 200; Related events
- AssumeRole AWS STS
- CreateAccessKey IAM
- GetSessionToken AWS STS
- ListBuckets S3
- CreateUser IAM
Detect it
MITRE ATT&CK: T1087.004
Frequently Asked Questions
- Can an IAM policy block GetCallerIdentity?
- No. AWS documents that no permissions are required and that an explicit deny on sts:GetCallerIdentity does not stop the call, because the same information is returned when access is denied. Anyone holding a valid access key can always run it, which is why monitoring, not prevention, is the control here.
- Why is responseElements empty in a GetCallerIdentity event?
- In the recorded GetCallerIdentity events both requestParameters and responseElements are null, so CloudTrail does not repeat the account, ARN and user ID the call returned. Nothing is lost, because that answer is the identity already in the record's userIdentity block, the identity of the credentials that made the call.
- Is a GetCallerIdentity call suspicious on its own?
- Not usually. The AWS CLI, infrastructure-as-code tools and CI jobs call it routinely to learn which account they run in. What matters is the context, a long-term key used from an address or client it has never used, and what follows, such as a burst of List and Describe calls or new users and keys.
- What should I do when a key's GetCallerIdentity comes from an unknown address?
- List every call made with that userIdentity.accessKeyId since, check whether the owner recognises the address and client, and if not deactivate the key and look for persistence the key may already have created, such as new access keys, users, login profiles or changed role trust policies.
Find every GetCallerIdentity in your own CloudTrail logs
Load CloudTrail logs (the Records files CloudTrail writes to S3, or aws cloudtrail lookup-events output) into the free browser analyzer: logging changes, new access keys, administrator grants, root activity and failed console sign-ins are flagged, with event names linked to their pages here. Nothing is uploaded.