As we have discussed previously, adversaries who gain access to AWS accounts can abuse the Security Token Service (STS) to grant themselves short-term tokens and assume roles before performing malicious activity within that cloud environment. Historically, the main way to attribute activity performed by a user after they have assumed a role was to track access keys every time a user assumed a new role. While this can work, there are also known workarounds to obfuscate activity and break the trail of access keys, making it difficult to trace activity back to the compromised user account.
However, AWS has an attribute in STS called SourceIdentity that allows defenders to trace all user activity in AssumeRole sessions back to corporate identities such as usernames or email addresses.
What is SourceIdentity?
So, what is this SourceIdentity attribute and what does it do for you? It allows you to require a field whenever a user assumes a role in AWS that is akin to role session name or session tags. SourceIdentity stands apart from these other fields by the fact that it persists throughout all follow-on sessions the user creates. If they were to assume one role and then pivot to another, SourceIdentity would persist in the logs even if they didn’t specify it in the subsequent AssumeRole calls.
As with most fields in AWS, you can also specify SourceIdentity to be a specific value. When you configure the trust relationships for the roles, you can specify that SourceIdentity must be a username, email address, or any other identifier. You can also integrate it with your identity providers such as Okta, Ping, or OneLogin.
SourceIdentitystands apart from other AWS attributes by the fact that it persists throughout all follow-on sessions the user creates.
SourceIdentity in action
For this exercise, we will follow the user accessKeyIdTesting through twoAssumeRole calls and finally their execution of the GetCallerIdentity API event. The screenshot below outlines the basic steps of the process.
So what does that look like in the logs?
First AssumeRole event
aws sts assume-role --role-arn arn:aws:iam::<accountId>:role/accessKeyIdTesting --role-session-name sourceTesting --source-identity followMe --profile accessKeyTestingThe first event is the original AssumeRole, wherein we specify the SourceIdentity field as followMe. Normally you would want to set the condition that this field is some sort of corporate identity such as an email address or username.
Since we specified the SourceIdentity in the AssumeRole request, we can observe it in both the responseElements and the requestParameters. Since this is the original AssumeRole event, it is not present in the userIdentity field (keep this in mind for later).
Figure 1: AssumeRole event with SourceIdentity
In Figure 1 we can see that the new SourceIdentity field is present in both the requestParameters and the responseElements. The responseElements section should be the only one used to track SourceIdentity as it will always be present even if it is not part of the original request, as will be evident in the next event.
AssumeRole chain
aws sts assume-role --role-arn arn:aws:iam::<accountId>:role/lambdaManager --role-session-name sourceTesting --profile sourceIdenRoleThe user then pivots to another role from the first one without specifying SourceIdentity. In the logs, even though it wasn’t specified, it still shows up in the responseElements! This makes it so much easier to track what the user is doing during their entire session. Previously, you’d have to track the access keys and capture that for each pivot to the new role.
Figure 2: AssumeRole userIdentity elements
In Figure 2 above we can see that the SourceIdentity field is present under the userIdentity.sessionContext field and matches the value from the responseElement in Figure 1. This allows us to trace the activity of the two events.
Figure 3: AssumeRole part deux responseElements
In Figure 3 we can see that with this new AssumeRole call, the SourceIdentity was not specified in the command and therefore not included in the requestParameters. However, since it was included in the first call it is still supplied in the responseElements.
The API call
Finally, the user then in this final role executes the GetCallerIdentity command. In the CloudTrail event we can clearly see the SourceIdentity field in userIdentity parameters. We can trace this back to that specific user regardless of what roles they have assumed or what events they execute.
Figure 4: GetCallerIdentity
As was true with the second AssumeRole call, the SourceIdentity field exists in the userIdentity.sessionContext field. This allows us to trace back this activity from the API call all the way back to the AssumeRole call without having to rely on access keys. It will also allow us to just directly tie this API call to whichever user identifier we choose when we configure SourceIdentity to be required for all users.
So what?
Why is this attribute so helpful? Users may leverage several different roles while using services in AWS, and tracking who is doing what can be near impossible with medium-to-large AWS accounts. SourceIdentity allows defenders to baseline “normal” activity more easily. We don’t have to rely on temporary and random credential identifiers to track sessions anymore; SourceIdentity takes care of that for us.
With SourceIdentity we can simply compare anomalous behavior to previous sessions to make a determination if it is malicious or not. Determining what constitutes “normal” behavior is almost impossible in cloud environments unless admins have strict security policies and well named, very descriptive roles. We know that this will not always be the case, and enabling SourceIdentity will enable defenders to make determinations with significantly more confidence.




