InfoSec Write-ups – Medium–
When I started getting into AWS pentesting, one of the hardest things to fully understand was IAM. AWS documentation is usually great, but can be extensive, and IAM has a lot of similar terms. You have users, roles, groups, managed policies, inline policies, instance roles, etc…
This article will try to shine some light on the subject, as well as some ways to enumerate this information with different tools.
Warning: the post will contain only theory, I’ll publish a write up with practical examples about this next week, but you’ll need the knowledge in this one before the “fun” one.
AWS defines IAM as a way for you to manage access to AWS services and resources securely. It all comes down to permissions. IAM is a way of managing permissions to access your cloud resources. This permissions are assigned to entities. Entities are things to which you can assign permissions to. There are 3 possible entities in IAM:
A user is a representation of the person or service who interacts with AWS.
A group is a collection of IAM users.
Both of these concepts are fairly intuitive, and similar to the ones found in other environments such as Active Directory.
A role similar to a user, in that it’s an identity with permission policies that determine what the identity can and cannot do in AWS. However, a role does not have any credentials (password or access keys) associated with it. Instead of being uniquely associated with one person, a role is intended to be assumable by anyone who needs it. (source: https://docs.aws.amazon.com/IAM/latest/UserGuide/id.html)
Most of these definitions come from AWS’s official documentation. But here’s where it gets a little more complicated. You assign permissions to entities through policies. Policies come in two flavours:
- Managed policies
- Inline policies (think of them as unmanaged policies)
The difference between them is that Managed policies are stand alone policies, defined on their own. They are not associated with any specific entity, and they can be attached to multiple entities.
Inline policies (or unmanaged policies) are policies that are embedded in an IAM identity (a user, group, or role). The policy is an inherent part of the identity. It cannot be assigned to any other entities. Think of them like a specific attribute of an entity.
Managed policies are recommended over Inline policies because they are easier to manage and audit. You can have a thousand users with the same managed policy, and one change on the policy would affect them all. You would only have to audit that one policy. If you had inline policies for each user, you would have to audit 1000 different policies, and you wouldn’t have a way of modifying all at once.
There’s one more distinction that you need to be aware of. Managed policies can also be divided into two categories:
- Managed policies (lets call them AWS Managed policies for disambiguity)
- Customer Managed policies
The difference is simple. Managed policies are managed by AWS. They define common roles that you might need, and you can’t modify them.
Customer Managed policies are policies that the customer creates and manages. You can define custom policies, adapted to your specific needs.
Another distinction you can make is between:
- Identity-based policies
- Resource-based policies
Remember when I said before that entities are things to which you can assign policies? Well, this is somewhat incomplete. Identities are things to which you can assign Identity-based policies. These are assigned to Users, Groups and Roles.
There is another kind of policies called resource-based policies, which can only be assigned to resources. Think things like AWS services (S3 buckets, SQS queues, etc..). Also, EC2 instances. Whenever you have a resource that needs specific permissions to do a task, you can use resource-based policies.
There’s one thing that you need to know, and this is very important. Resources can be granted permissions through resource-based policies (directly attached) as well as through roles. At the beginning I said roles can be assumed by anybody who needs it. This also includes instances, and AWS services (https://docs.amazonaws.cn/en_us/IAM/latest/UserGuide/id_roles_compare-resource-policies.html).
Why is this knowledge important you ask? Well, one of the most frequent ways of pivoting access in AWS environments is by hijacking instance roles.
Whenever you want an instance to have certain permissions, you can grant it permissions to assume a specific role. This is called an instance role. You can apply only one role to an instance, but you can have multiple instances with the same role. The temporary credentials associated with this role are saved in what’s called the instance metadata. You can access this metadata from inside the instance by doing an http request to
This request can be performed by any user inside the instance. If the instance role has high privileges associated with it, any user with access to the instance can hijack it. I’ll show how in the next story with a real life example.
This also opens a new vector of exploitation for open redirects / SSRF vulnerabilities. If you find one in the instance, you can request its metadata and hijack that role.
Assuming you can get an AccesKey, a SecretAccessKey and an ExpirationToken, you’ll want to enumerate your available permissions and find a way to maintain access because this credentials have an expiration date (as you can see in the picture) and there’s a chance that they won’t be renewed. By default this time is one hour, so you better hurry! The best tool I’ve found to do this is enumerate-iam.py (https://github.com/andresriancho/enumerate-iam), developed by Andres Riancho. You can use it like this (session token is optional)
enumerate-iam.py --access-key "ACCESSKEY" --secret-key "SECRETKEY" (--session-token "$AWS_SESSION_TOKEN")
IAM enumeration tools
Now that we understand how most of IAM works, we can start to learn how to enumerate it. We have to different ways:
If you like working with a graphical user interface, you can use cs-suite(https://github.com/SecurityFTW/cs-suite). This groups several other tools to perform a wholesome audit. You can also use ScoutSuite, which is the successor of scout2, one of the tools cs-suite uses (https://github.com/nccgroup/ScoutSuite).
I normally run it through docker like this:
cat > .aws/config <<EOF
output = json
region = us-east-1
cat > .aws/credentials <<EOF
aws_access_key_id = XXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXX
docker run -v `pwd`/.aws:/root/.aws -v `pwd`/reports:/app/reports securityftw/cs-suite -env aws
After this is done running, you’ll get a graphical report in /tmp/reports.
This will show you a lot of useful information including public attack surface, generally insecure permissions and weak configurations. It’ll also give you a summary of all the cloud’s resources.
This approach is appropriate for blue teams looking to harden its infrastructure. I personally find it a little noisy for red teaming. Its similar to using a vulnerability scanner. Sure, Nessus might find a lot of things that Nmap can’t, but it also alerts every SOC in a hundred miles of your presence. When doing red team assessments, I usually prefer to do manual enumeration through the CLI.
This approach causes a lot less activity in the logs, but you’ll have to correlate the information yourself. Most of the things you can get from cs-suite you can manually get from awscli. I took the time at the beginning of the story to explain you what the differences in roles and policies are because you’re going to need different commands to query them.
The usual enumeration process I do is
- Define the profile we’re going to use.
To prevent typing your credentials on every command, you can do:
aws configure --profile test
After pasting your credentials, you’ll be able to run all the following commands using the profile flag. This also makes easier working with multiple profiles at the same time.
2. Get the managed policies available to our user.
To get the details of each policy you need the policy version and the policy arn. You can get the arn of policies available to your profile with:
aws --profile "$profile" iam list-policies | jq -r ".Policies.Arn"
You can get a specific policy version with
aws --profile "$profile" iam get-policy --policy-arn "$i" --query "Policy.DefaultVersionId" --output text
You can group both of these commands to get all relevant configuration with:
profile="test"; for i in $(aws --profile "$profile" iam list-policies | jq -r '.Policies.Arn'); do echo "Describing policy $i" && aws --profile "$profile" iam get-policy-version --policy-arn "$i" --version-id $(aws --profile "$profile" iam get-policy --policy-arn "$i" --query 'Policy.DefaultVersionId' --output text); done | tee /tmp/policies.log
As you can see, each policy is composed of an Effect (allow/deny) + Action (what operation do you wish to perform) + Resource (the ARN of the resource affected by the operation). You can optionally have a Condition associated too.
You should be looking for something like this:
Describing policy arn:aws:iam::aws:policy/AdministratorAccess
This policy allows you to do anything over any resource. Full access.
3. See which entities have the interesting policies attached.
This should give you an idea of which Users/Groups/Roles to target. You can enumerate assignments of managed policies with
#List Managed User policies
aws --profile "test" iam list-attached-user-policies --user-name "test-user"
#List Managed Group policies
aws --profile "test" iam list-attached-group-policies --group-name "test-group"
#List Managed Role policies
aws --profile "test" iam list-attached-role-policies --role-name "test-role"
You also have commands to enumerate inline policies. You can do this with
#List Inline User policies
aws --profile "test" iam list-user-policies --user-name "test-user"
#List Inline Group policies
aws --profile "test" iam list-group-policies --group-name "test-group"
#List Inline Role policies
aws --profile "test" iam list-role-policies --role-name "test-role"
Lastly, to get the details of Inline policies, you can do:
#Describe Inline User policies
aws --profile "test" iam get-user-policy --user-name "test-user" --policy-name "test-policy"
#Describe Inline Group policies
aws --profile "test" iam get-group-policy --group-name "test-group" --policy-name "test-policy"
#Describe Inline Role policies
aws --profile "test" iam get-role-policy --role-name "test-role" --policy-name "test-policy"
There’s another thing that you need to know. When roles are created, they have a feature called trust relationships. A trust relation specifies who can assume that role in a JSON document called the assume role policy.
An assume role policy would look something like this.
You can manually query the assume role policy associated with a specific role with:
aws --profile "test" iam get-role --role-name "test-role"
This example assume role policy allows any ec2 resource to assume the role. One clarification I should make. This doesn’t mean that you can assume the role from any ec2 instance. You still need an admin to
1. Create an Instance Profile
2. Associate the role to that Instance Profile
3. Associate the Instance Profile with the specific instance you want to use
Assuming that the role has already been created, and that it has the interesting policy already attached, the commands you would need to run to perform these 3 steps would be:
aws iam create-instance-profile --instance-profile-name YourNewRole-Instance-Profile
aws iam add-role-to-instance-profile --role-name YourNewRole --instance-profile-name YourNewRole-Instance-Profile
aws ec2 associate-iam-instance-profile --instance-id YourInstanceId --iam-instance-profile Name=YourNewRole-Instance-Profile
(For more information, go to https://aws.amazon.com/blogs/security/new-attach-an-aws-iam-role-to-an-existing-amazon-ec2-instance-by-using-the-aws-cli/?sc_channel=sm&sc_campaign=rolesforrunninginstances&sc_publisher=tw&sc_medium=social&sc_content=read-post&sc_country=global&sc_geo=global&sc_category=ec2&sc_outcome=launch)
There’s one thing that I haven’t been able to do from aws cli, and that is to list every resource who has assumed a specific role. If you have access to an instance, you can see the roles it has assumed with
aws --profile test sts get-caller-identity
You could also see the role with a curl to the instance metadata:
This has been a lengthy article. I’m sorry if it got a little tedious, but I couldn’t find a concise explanation of most of IAM features, or a command reference for manual enumeration.
If you want extra material on AWS pentesting, there are 3 resources I would like to recommend. All three are written by people from Rinho Labs, which has pioneered a lot of the research on AWS pentesting.
First, this article covers 21 privilege escalation methodologies, and it’s excellently written: https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/
The second resource is actually an AWS exploitation framework called Pacu which I’ll probably end up writing another story about. This is one of the best tools I’ve found. https://github.com/RhinoSecurityLabs/pacu
Lastly, theres a great book about AWS Pentesting called “Hands-On AWS Penetration Testing with Kali Linux” (https://www.amazon.com/-/es/Karl-Gilbert/dp/1789136725), written by Karl Gilbert and Benjamin Caudill (one of the developers of Pacu). I frequently use this book for brushing up on certain topics that might come up on an assessment.