AWS privilege escalation: exploring odd features of the Trust Policy

Pawel Rzepa
Inside the Tech by SoftServe
4 min readAug 26, 2021

--

IAM roles are commonly used, for example, to grant access to AWS service, account, or federated identity. Each role has a document associated with it, which is called a Trust Policy. This document specifies who can assume the role and under what conditions it’s allowed or denied. The Trust Policy is often a subject of special interest among attackers, because it may be configured to work as a backdoor to compromised AWS account or even an entry point for anonymous users. However, in this blog post, I want to focus on some inconsistencies in the Trust Policy access model, which in consequence can be used for privilege escalation.

Abusing the Trust Policy

In general, an IAM role can be assumed when the 2 following requirements are met:

  1. The target role has a trust relationship with the principal, which attempts to assume the role.
  2. The principal attempting to assume the role has the sts:AssumeRole permission.

However, there are exceptional situations when the second requirement is not essential. A Trust Policy is in fact an IAM resource-based policy. That means that within a single AWS account you only need permission from resource-based policy OR principal policy (across AWS accounts you need both). There are certain use cases when Trust Policy can “grant” the sts:AssumeRole permission and the principal doesn’t need to have explicitly such permission and still can assume a role. That may sound confusing, so please let me demonstrate this mechanism in an example.

Escalating privileges through a chain of trust relationships

Let’s assume, there’s an AWS account, which has 2 groups of IAM users: the unprivileged Operators and the Admins (who can do anything they want and also have sts:AssumeRole permission to assume any role). Furthermore, this demo AWS account has the role called maintenance-role, which can be assumed by administrators in the whole organization. This is defined by the following Trust Policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-abcd12efg1"
}
}
}
]
}

I’ve seen such Trust Policy several times in the wild and it’s even presented in the official AWS Security Blog (s̵e̵e̵ ̵t̵h̵e̵ ̵”̵L̵i̵m̵i̵t̵i̵n̵g̵ ̵o̵r̵ ̵e̵x̵t̵e̵n̵d̵i̵n̵g̵ ̵a̵c̵c̵e̵s̵s̵ ̵t̵o̵ ̵a̵ ̵r̵o̵l̵e̵ ̵b̵a̵s̵e̵d̵ ̵o̵n̵ ̵A̵W̵S̵ ̵O̵r̵g̵a̵n̵i̵z̵a̵t̵i̵o̵n̵s̵”̵ section of this article; please note that I refer to the archived version of this blog post, because after publishing this article the above-mentioned section was removed; big-ups to AWS team for really quick action!). Surprisingly, the wildcard character in the Principal section causes the strange behavior, which is: every principal in the ‘o-abcd12efg1’ organization, and in the same AWS account as the assumed role, can assume the role WITHOUT sts:AssumeRolepermission😯

Please note that in the above-mentioned use case Trust Policy behaves like a resource-based policy. It “grants” sts:AssumeRole permission to the principal in the same AWS account. However, principals in other AWS accounts and under the same OU will need the sts:AssumeRole permission, because resource-based policies don’t “grant” permissions cross-account (only under the same AWS account).

In other words, any unprivileged principal (even IAM users who have no permissions at all), who are in o-abcd12efg1 organization and in the same AWS account as the assumed role, can assume the maintenance-role. Pretty scary, huh?

Furthermore, let’s assume there’s an additional admin-role, which can be assumed by the maintenance-role. This can be done by specifying the following Trust Policy associated with admin-role:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/maintenance-role"
},
"Action": "sts:AssumeRole",
"Condition": {}
}
]
}

As you may read in the Hacking the Cloud article, if the target role’s trust relationship is tied to a specific role, then the base role does NOT need to have sts:AssumeRolepermission😯

To sum up, the attack flow to escalate privileges from nothing to administrator is the following:

  1. The unprivileged IAM user assumes the maintenance-role. The user doesn’t require sts:AssumeRole permission, because the role’s trust relationship contains the wildcard character.
  2. The maintenance-role entity assumes the admin-role. There’s no need to have sts:AssumeRole permission because the admin-role’s trust relationship refers to the maintenance-role’s ARN.

Conclusions

IMHO missing the requirement of having explicitly the sts:AssumeRole permission in the use cases mentioned above is a kind of inconsistency in the Trust Policy model, which may result in a serious security threat. It isn’t intuitive, so I’m not surprised that some administrators may unwittingly configure IAM roles in an insecure way. Quoting Murphy’s law:

“Anything that can go wrong will go wrong”

Do you know any other ways of abusing a Trust Policy, which wasn’t mentioned in this article? If so, please let me know in the comments or on my Twitter or LinkedIn.

--

--

Pawel Rzepa
Inside the Tech by SoftServe

Interested in pentesting and cloud security | OSCP | eMAPT | AWS SAA | AWS CSS