Introduction: IAM User Accounts
In our AWS IAM post from last week, we highlighted a technique that penetration testers can use to automate the process of enumerating the IAM roles of other AWS accounts. As long as the attacker knows the victim’s AWS account ID, they can discover the role names and even assume them to compromise the account if the AssumeRole permissions are misconfigured.
This post is an expansion of the last, as we are detailing a similar technique that pentesters can use to discover the IAM users of a target AWS account. Once again, all that a motivated adversary requires is knowledge of the account ID. Every AWS account is uniquely identified by this 12 digit number, and while this ID is usually kept private, they can all too easily be exposed to the public. In our previous AWS penetration tests, we have commonly found AWS account IDs leaked in error messages, forums, and even GitHub and other code repositories.
With a confirmed AccountID + IAM username pair, an attacker is much closer to successfully bruteforcing AWS credentials.
IAM Exploration: UpdateAssumeRolePolicy
The UpdateAssumeRolePolicy document is a JSON file that specifies the permissions dictating which users or services can or cannot assume a given role. Using these policies, one can explicitly allow or deny accounts or users access.
A typical AssumeRolePolicyDocument, showing a placheolder AWS Account ID and "root" as the associated user (always valid)
Enumerating Users through UpdateAssumeRolePolicy
During our AWS research with assuming roles, we observed an error message that occurred when updating an AssumeRole policy document with a mistyped username, similar to the following:
A verbose error message returned after specifying a valid user account with an invalid IAM user.
After validating the consistency of the error messages, we found that any invalid username returned an error message, while valid ones did not. This information leakage allows us to confirm the existence of a given IAM users, provided we have a given AWS Account ID (which we’ve previously established is semi-public).
After testing this process a number of times, we upgraded to programmatic user verification to test the limits of the technique. In all of the tests we performed, no account lockouts or CloudTrail Logs were encountered.
Automating the Process with Python
This process of enumerating users via this method can now be fully automated at scale by using the standalone script we have just released on GitHub.
The script accepts a valid AWS account ID and tries to enumerate existing IAM users within that account. It does so by trying to update the AssumeRole policy document of the role that you pass into the “—role-name” option. For your safety, it updates the policy with an explicit deny against the AWS account/IAM user, so that no security holes are opened in your account during enumeration.
By default, the script utilizes the included list of 1100+ common IAM usernames, including first names, AWS service names (ec2, lambda, rds, etc) and associated 3rd party tools. This can of course easily be changed by specifying another wordlist with the “—word-list” option. To improve your chances of success, custom wordlists (such as CeWL) are recommended.
iam_user_enum.py [-h] [-s] [-p PROFILE] [-w WORD_LIST] [-r ROLE_NAME] [-i ACCOUNT_ID]
-h, --help show this help message and exit -s, --self-check Perform a self check against your own AWS account. This flag will skip the bruteforcing and instead list out all IAM users in your account and whether or not they have MFA enabled. This will give you an idea of potential targets in your account and how vulnerable they are to an attack of this kind. -p PROFILE, --profile PROFILE The AWS CLI profile to use for making API calls. This is usually stored under ~/.aws/credentials. You will be prompted by default. -w WORD_LIST, --word-list WORD_LIST File path to a different word list to use. There is a default word list with 1063 words. The word list should contain words, one on each line, to use to try and guess IAM role names. Role names ARE case- sensitive. -r ROLE_NAME, --role-name ROLE_NAME The name of a valid role in the current users account to try and update the AssumeRole policy document for. -i ACCOUNT_ID, --account-id ACCOUNT_ID The AWS account ID of the target account (12 numeric characters).
The following shows the script in use and the output produced when it successfully identifies a number of IAM users:
The script using the role "Lambda", the account ID 216xxxxxxx41, a non-default wordlist named "names.txt", and the "default" AWS CLI profile for credentials.
NOTE: It is recommended to use test/sandbox AWS access keys for this script, as it will spam the local (attacker) CloudTrail with “iam:UpdateAssumeRolePolicy” logs (the target account CloudTrail will not have these log entries). The keys used must have the iam:UpdateAssumeRolePolicy permission on the role that you pass into “—role-name” to be able to identify a valid IAM user.
Pacu AWS Module
As with previous AWS attack scripts we’ve released, this IAM username enumeration script has also been integrated as a module into Pacu — the AWS exploitation framework. Now with over 30 modules ranging from reconnaissance to data exfiltration, Pacu offers a range of tools for AWS penetration testing.
Mitigating the Risk
While this username enumeration vector is low risk in itself – most useful as part of a larger campaign – the underlying issue lies in AWS’ verbose error messaging, so all AWS users are affected.
Even so, we’ve included an option in the iam_user_enum script to help gauge your susceptibility to IAM attacks. Run the “—self-check” option to pull all usernames for the associated account and if they have two-factor authentication (2FA) enabled.
This security mechanism ensures that even if IAM usernames are enumerated and passwords are compromised, there’s a second authentication check to prevent account hijacking. 2FA is highly recommended for all AWS users, if not already enabled.
A combination of verbose error messaging and the occasionally leaked account ID enables unbridled AWS asset enumeration. In these last two posts we have laid out how both IAM roles and users are susceptible to providing additional information to attackers. Enumeration by itself is rarely compromising on its own, but the information it reveals can be leveraged to launch more sophisticated — and damaging — attacks against your cloud environment.
If you are interested in leveraging other common AWS misconfigurations, try out Pacu – the open source AWS exploitation framework for pentesters— which incorporates this script as a dedicated module, called “iam_user_enum”.
If you need a safe, inexpensive AWS infrastructure to test out these new tools, consider CloudGoat — the vulnerable-by-design AWS environment.