AWS - IAM & STS Unauthenticated Enum

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Enumerate Roles & Usernames in an account

Assume Role Brute-Force

This technique doesn't work anymore as if the role exists or not you always get this error:

An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::947247140022:user/testenv is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::429217632764:role/account-balanceasdas

You can test this running:

aws sts assume-role --role-arn arn:aws:iam::412345678909:role/superadmin --role-session-name s3-access-example

Attempting to assume a role without the necessary permissions triggers an AWS error message. For instance, if unauthorized, AWS might return:

An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::012345678901:user/MyUser is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::111111111111:role/aws-service-role/rds.amazonaws.com/AWSServiceRoleForRDS

This message confirms the role's existence but indicates that its assume role policy does not permit your assumption. In contrast, trying to assume a non-existent role leads to a different error:

An error occurred (AccessDenied) when calling the AssumeRole operation: Not authorized to perform sts:AssumeRole

Interestingly, this method of discerning between existing and non-existing roles is applicable even across different AWS accounts. With a valid AWS account ID and a targeted wordlist, one can enumerate the roles present in the account without facing any inherent limitations.

You can use this script to enumerate potential principals abusing this issue.

Trust Policies: Brute-Force Cross Account roles and users

Configuring or updating an IAM role's trust policy involves defining which AWS resources or services are permitted to assume that role and obtain temporary credentials. If the specified resource in the policy exists, the trust policy saves successfully. However, if the resource does not exist, an error is generated, indicating that an invalid principal was provided.

Note that in that resource you could specify a cross account role or user:

  • arn:aws:iam::acc_id:role/role_name

  • arn:aws:iam::acc_id:user/user_name

This is a policy example:

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

GUI

That is the error you will find if you uses a role that doesn't exist. If the role exist, the policy will be saved without any errors. (The error is for update, but it also works when creating)

CLI

### You could also use: aws iam update-assume-role-policy
# When it works
aws iam create-role --role-name Test-Role --assume-role-policy-document file://a.json
{
    "Role": {
        "Path": "/",
        "RoleName": "Test-Role",
        "RoleId": "AROA5ZDCUJS3DVEIYOB73",
        "Arn": "arn:aws:iam::947247140022:role/Test-Role",
        "CreateDate": "2022-05-03T20:50:04Z",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::316584767888:role/account-balance"
                    },
                    "Action": [
                        "sts:AssumeRole"
                    ]
                }
            ]
        }
    }
}

# When it doesn't work
aws iam create-role --role-name Test-Role2 --assume-role-policy-document file://a.json
An error occurred (MalformedPolicyDocument) when calling the CreateRole operation: Invalid principal in policy: "AWS":"arn:aws:iam::316584767888:role/account-balanceefd23f2"

You can automate this process with https://github.com/carlospolop/aws_tools

  • bash unauth_iam.sh -t user -i 316584767888 -r TestRole -w ./unauth_wordlist.txt

Our using Pacu:

  • run iam__enum_users --role-name admin --account-id 229736458923 --word-list /tmp/names.txt

  • run iam__enum_roles --role-name admin --account-id 229736458923 --word-list /tmp/names.txt

  • The admin role used in the example is a role in your account to by impersonated by pacu to create the policies it needs to create for the enumeration

Privesc

In the case the role was bad configured an allows anyone to assume it:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

The attacker could just assume it.

Third Party OIDC Federation

Imagine that you manage to read a Github Actions workflow that is accessing a role inside AWS. This trust might give access to a role with the following trust policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<acc_id>:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}

This trust policy might be correct, but the lack of more conditions should make you distrust it. This is because the previous role can be assumed by ANYONE from Github Actions! You should specify in the conditions also other things such as org name, repo name, env, brach...

Another potential misconfiguration is to add a condition like the following:

"StringLike": {
    "token.actions.githubusercontent.com:sub": "repo:org_name*:*"
}

Note that wildcard (*) before the colon (:). You can create an org such as org_name1 and assume the role from a Github Action.

References

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Last updated