# Module 24: Enumerating AWS Cloud Infrastructure

## About the Public Cloud Labs

1. The lab environment should not be used for activities not described or requested in the learning materials you encounter. It is not designed to serve as a playground to test additional items that are out of the scope of the Learning Module.
2. The lab environment should not be used to take action against any asset external to the lab. This is specifically noteworthy because some Modules may describe or even demonstrate attacks against vulnerable cloud deployments for the purpose of describing how those deployments can be secured.
3. Existing rules and requirements against sharing OffSec training materials still apply. Credentials and other details of the lab are not meant to be shared. OffSec monitors activity in the Public Cloud Labs (including resource usage) and monitors for abnormal events that are not related to activities described in the learning modules.

{% hint style="danger" %}
Activities that are flagged as suspicious will result in an investigation. If the investigation determines that a student acted outside of the guidelines described above, or otherwise intentionally abused the OffSec Public Cloud Labs, OffSec may choose to rescind that learner's access to the OffSec Public Cloud Labs and/or terminate the learner's account.
{% endhint %}

## Reconnaissance of Cloud Resources on the Internet

### Accessing the Lab

Deployment will take a few minutes to complete and, once complete, the additional services might take 5-10 minutes to start.

Once the lab finishes its deployment, we'll receive some pieces of information we'll need later while working in the lab.

* Public DNS IP Address
* Domain name of the target
* Credentials for the IAM user *attacker*
  * ACCESS\_KEY\_ID
  * SECRET\_ACCESS\_KEY

We must configure the DNS in our local environment to use the public DNS set for this lab:

```bash
kali@kali:~$ cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 44.205.254.229
nameserver 1.1.1.1
```

{% hint style="info" %}
We should also keep in mind that the public IP address of the DNS server will change every time we restart the lab, and we'll need to run this configuration again.
{% endhint %}

### Domain and Subdomain Reconnaissance

Querying the *authoritative* DNS servers for the offseclab.io domain:

```bash
kali@kali:~$ host -t ns offseclab.io
offseclab.io name server ns-1536.awsdns-00.co.uk.
offseclab.io name server ns-512.awsdns-00.net.
offseclab.io name server ns-0.awsdns-00.com.
offseclab.io name server ns-1024.awsdns-00.org.
```

Validating who owns the domains found:

```bash
kali@kali:~$ whois awsdns-00.com | grep "Registrant Organization"
Registrant Organization: Amazon Technologies, Inc.
```

Querying the public IP address of our target:

```bash
kali@kali:~$ host www.offseclab.io
www.offseclab.io has address 52.70.117.69
```

Performing a *reverse* DNS lookup:

{% code overflow="wrap" %}

```bash
kali@kali:~$ host 52.70.117.69
69.117.70.52.in-addr.arpa domain name pointer ec2-52-70-117-69.compute-1.amazonaws.com

kali@kali:~$ whois 52.70.117.69 | grep "OrgName"
OrgName:        Amazon Technologies Inc.
```

{% endcode %}

Using **dnsenum** to auomate some of this dns reconnaissance:

```bash
kali@kali:~$ dnsenum offseclab.io --threads 100
dnsenum VERSION:1.2.6

-----   offseclab.io   -----


Host's addresses:
__________________

offseclab.io.                            60       IN    A        52.70.117.69

Name Servers:
______________

ns-1536.awsdns-00.co.uk.                 0        IN    A        205.251.198.0
ns-0.awsdns-00.com.                      0        IN    A        205.251.192.0
ns-512.awsdns-00.net.                    0        IN    A        205.251.194.0
ns-1024.awsdns-00.org.                   0        IN    A        205.251.196.0


Mail (MX) Servers:
___________________



Trying Zone Transfers and getting Bind Versions:
_________________________________________________

Trying Zone Transfer for offseclab.io on ns-512.awsdns-00.net ...
AXFR record query failed: corrupt packet

Trying Zone Transfer for offseclab.io on ns-1024.awsdns-00.org ...
AXFR record query failed: corrupt packet

Trying Zone Transfer for offseclab.io on ns-0.awsdns-00.com ...
AXFR record query failed: corrupt packet

Trying Zone Transfer for offseclab.io on ns-1536.awsdns-00.co.uk ...
AXFR record query failed: corrupt packet


Brute forcing with /usr/share/dnsenum/dns.txt:
_______________________________________________
mail.offseclab.io.                       60       IN    A        52.70.117.69
www.offseclab.io.                        60       IN    A        52.70.117.69
...
```

### Service-specific Domains

*Not much to add, of note Amazon ec2 instances tend to have a URL beginning with ec2. Same goes for s3 buckets.*

**cloud-enum** is a useful tool to perform OSINT across multiple CSPs. The cloud-enum tool will search through several public CSPs for resources containing a keyword specified using the **--keyword KEYWORD** (**-k KEYWORD**) parameter. We can specify multiple keyword arguments, or we can specify a list with the **--keyfile KEYFILE** (**-kf KEYFILE**) parameter.

We can also use the **--mutations** (**-m**) option to specify a file to add extra words to the keyword. If we don't specify any file, the **/usr/lib/cloud-enum/enum\_tools/fuzz.txt** file is used by default. We can disable this option using the **--quickscan** (**-qs**) parameter.

*Using **cloud-enum** to search for more buckets belonging to offseclab.io:*

{% code overflow="wrap" %}

```bash
kali@kali:~$ cloud_enum -k offseclab-assets-public-axevtewi --quickscan --disable-azure --disable-gcp

...

Keywords:    offseclab-assets-public-axevtewi
Mutations:   NONE! (Using quickscan)
Brute-list:  /usr/lib/cloud-enum/enum_tools/fuzz.txt

[+] Mutated results: 1 items

++++++++++++++++++++++++++
      amazon checks
++++++++++++++++++++++++++

[+] Checking for S3 buckets
  OPEN S3 BUCKET: http://offseclab-assets-public-axevtewi.s3.amazonaws.com/
      FILES:
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/offseclab-assets-public-axevtewi
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/amethyst-expanded.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/amethyst.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/logo.svg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/pic02.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/pic05.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/pic13.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/ruby-expanded.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/ruby.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/saphire-expanded.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/saphire.jpg
                            
                            
 Elapsed time: 00:00:00

[+] Checking for AWS Apps
[*] Brute-forcing a list of 1 possible DNS names
                            
 Elapsed time: 00:00:00


[+] All done, happy hacking!
```

{% endcode %}

Using a custom keyfile for cloud-enum:

{% code overflow="wrap" %}

```bash
kali@kali:~$ for key in "public" "private" "dev" "prod" "development" "production"; do echo "offseclab-assets-$key-axevtewi"; done | tee /tmp/keyfile.txt
offseclab-assets-public-axevtewi
offseclab-assets-private-axevtewi
offseclab-assets-dev-axevtewi
offseclab-assets-prod-axevtewi
offseclab-assets-development-axevtewi
offseclab-assets-production-axevtewi

kali@kali:~$ cloud_enum -kf /tmp/keyfile.txt -qs --disable-azure --disable-gcp

...

Keywords:    offseclab-assets-public-axevtewi, offseclab-assets-private-axevtewi, offseclab-assets-dev-axevtewi, offseclab-assets-prod-axevtewi, offseclab-assets-development-axevtewi, offseclab-assets-production-axevtewi
Mutations:   NONE! (Using quickscan)
Brute-list:  /usr/lib/cloud-enum/enum_tools/fuzz.txt

[+] Mutated results: 6 items

++++++++++++++++++++++++++
      amazon checks
++++++++++++++++++++++++++

[+] Checking for S3 buckets
  OPEN S3 BUCKET: http://offseclab-assets-public-axevtewi.s3.amazonaws.com/
      FILES:
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/offseclab-assets-public-axevtewi
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/amethyst-expanded.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/amethyst.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/logo.svg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/pic02.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/pic05.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/pic13.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/ruby-expanded.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/ruby.jpg
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/saphire-expanded.png
      ->http://offseclab-assets-public-axevtewi.s3.amazonaws.com/sites/www/images/saphire.jpg
  Protected S3 Bucket: http://offseclab-assets-private-axevtewi.s3.amazonaws.com/
                            
 Elapsed time: 00:00:06

[+] Checking for AWS Apps
[*] Brute-forcing a list of 6 possible DNS names
                            
 Elapsed time: 00:00:00


[+] All done, happy hacking!
```

{% endcode %}

{% hint style="info" %}
Objects inside a bucket can be publicly accessible even if the bucket is not publicly accessible.
{% endhint %}

## Reconnaissance via Cloud Service Provider's API

### Preparing the Lab - Configure AWS CLI

Configuring **awscli**:

```bash
kali@kali:~$ sudo apt update
...

kali@kali:~$ sudo apt install -y awscli
...
The following NEW packages will be installed:
  awscli docutils-common python3-awscrt python3-docutils python3-jmespath python3-roman
(Reading database ... 461429 files and directories currently installed.)
...

kali@kali:~$ aws configure --profile attacker
AWS Access Key ID []: AKIAQO...
AWS Secret Access Key []: cOGzm...
Default region name []: us-east-1
Default output format []: json

kali@kali:~$ aws --profile attacker sts get-caller-identity
{
    "UserId": "AIDAQOMAIGYU5VFQCHOI4",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/attacker"
}
```

### Publicly Shared Resources

Listing all AMIs  that our account can read, that are provided by Amazon that are public, including self-owned public AMIs:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile attacker ec2 describe-images --owners amazon --executable-users all
{
    "Images": [
        {
            "Architecture": "x86_64",
            "CreationDate": "2022-06-29T09:46:55.000Z",
            "ImageId": "ami-0d4f490f4e62171b4",
            "ImageLocation": "amazon/Deep Learning Base AMI (Amazon Linux 2) Version 53.4",
            "ImageType": "machine",
            "Public": true,
            "OwnerId": "898082745236",
            "PlatformDetails": "Linux/UNIX",
            "UsageOperation": "RunInstances",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "Iops": 3000,
                        "SnapshotId": "snap-0ce7f231ea72dd0ea",
                        "VolumeSize": 100,
...
```

{% endcode %}

Filtering for AMIs with a name including "Offseclab":

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile attacker ec2 describe-images --executable-users all --filters "Name=name,Values=*Offseclab*"
{
    "Images": [
        {
            "Architecture": "x86_64",
            "CreationDate": "2023-08-05T19:43:29.000Z",
            "ImageId": "ami-0854d94958c0a17e6",
            "ImageLocation": "123456789012/Offseclab Base AMI",
            "ImageType": "machine",
            "Public": true,
            "OwnerId": "123456789012",
            "PlatformDetails": "Linux/UNIX",
            "UsageOperation": "RunInstances",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "DeleteOnTermination": true,
                        "SnapshotId": "snap-098dc18c797e4f255",
                        "VolumeSize": 8,
                        "VolumeType": "gp2",
                        "Encrypted": false
                    }
                }
            ],
            "EnaSupport": true,
            "Hypervisor": "xen",
            "Name": "Offseclab Base AMI",
            "RootDeviceName": "/dev/xvda",
            "RootDeviceType": "ebs",
            "SriovNetSupport": "simple",
            "Tags": [
                {
                    "Key": "Name",
                    "Value": "Offseclab Base AMI"
                }
            ],
            "VirtualizationType": "hvm",
            "DeprecationTime": "2023-08-05T21:43:00.000Z"
        }
    ]
}
```

{% endcode %}

### Obtaining Account IDs from S3 Buckets

Steps to find the account ID from a publicly available S3 bucket:

1. Create an IAM user with a policy that only allows them to ls the S3 bucket if the account ID begins with x where x is 0-9.
2. As that user, attempt to ls the S3 bucket. If it's able to, then move to the next digit. Otherwise change the first digit for the account ID in teh policy and reapply.
3. Rinse & repeat.

There are also tools like ***s3-account-search*** to do this, however that tool uses roles rather than users to link the policy to the condition.

Example policy:

{% code title="policy-s3-read.json" overflow="wrap" %}

```json
{
     "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowResourceAccount",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject"
            ],
            "Resource": "*",
            "Condition": {
                "StringLike": {"s3:ResourceAccount": ["0*"]}
            }
        }
    ]
}
```

{% endcode %}

### Enumerating IAM Users in Other Accounts

Similar to the last section, we can use policies to determine if IAM accounts exist when we know an account ID. In this case we would assign the policy to our own private bucket. If no error returns after running the command to apply the policy, that means that the *cloudadmin* user exists in the target account.

Creating a dummy private bucket:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile attacker s3 mb s3://offseclab-dummy-bucket-$RANDOM-$RANDOM-$RANDOM
make_bucket: offseclab-dummy-bucket-28967-25641-13328
```

{% endcode %}

Example policy:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowUserToListBucket",
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::offseclab-dummy-bucket-28967-25641-13328",
            "Principal": {
                "AWS": ["arn:aws:iam::123456789012:user/cloudadmin"]
            },
            "Action": "s3:ListBucket"

        }
    ]
}
```

Applying the policy to our private bucket:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile attacker s3api put-bucket-policy --bucket offseclab-dummy-bucket-28967-25641-13328 --policy file://grant-s3-bucket-read.json 
```

{% endcode %}

Applying a policy where the user does not exist:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile attacker s3api put-bucket-policy --bucket offseclab-dummy-bucket-28967-25641-13328  --policy file://grant-s3-bucket-read-userDoNotExist.json 

An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: Invalid principal in policy
```

{% endcode %}

We can automate this process using ***pacu*** and a prebuilt list of users we'd like to test:

{% code overflow="wrap" %}

```bash
# Generating our list of users.
kali@kali:~$ echo -n "lab_admin
security_auditor
content_creator
student_access
lab_builder
instructor
network_config
monitoring_logging
backup_restore
content_editor" > /tmp/role-names.txt

# Installing pacu.
kali@kali:~$ sudo apt update
kali@kali:~$ sudo apt install pacu

# Starting pacu in interactive mode. 
kali@kali:~$ pacu
....
Database created at /root/.local/share/pacu/sqlite.db

What would you like to name this new session? offseclab
Session offseclab created.
...

# Setting keys from the AWS CLI, specifying the attacker role.
Pacu (offseclab:No Keys Set) > import_keys attacker
  Imported keys as "imported-attacker"
  
# Listing available modules.
Pacu (offseclab:imported-attacker) > ls
...
[Category: RECON_UNAUTH]

  iam__enum_roles
  iam__enum_users
...

# Getting information about the iam__enum_roles module
Pacu (offseclab:imported-attacker) > help iam__enum_roles
iam__enum_roles written by Spencer Gietzen of Rhino Security Labs.

usage: pacu [--word-list WORD_LIST] [--role-name ROLE_NAME] --account-id
            ACCOUNT_ID

This module takes in a valid AWS account ID and tries to enumerate existing
IAM roles within that account. It does so by trying to update the
AssumeRole policy document of the role that you pass into --role-name if
passed or newlycreated role. For your safety, it updates the policy with an
explicit deny against the AWS account/IAM role, so that no security holes
are opened in your account during enumeration. NOTE: It is recommended to
use personal AWS access keys for this script, as it will spam CloudTrail
with "iam:UpdateAssumeRolePolicy" logs and a few "sts:AssumeRole" logs. The
target account will not see anything in their logs though, unless you find
a misconfigured role that allows you to assume it. 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 role and the sts:AssumeRole
permission to try and request credentials for any enumerated roles.
...

# Running the module with our custom name list.
Pacu (offseclab:imported-attacker) > run iam__enum_roles --word-list /tmp/role-names.txt --account-id 123456789012
  Running module iam__enum_roles...
...

[iam__enum_roles] Targeting account ID: 123456789012

[iam__enum_roles] Starting role enumeration...


[iam__enum_roles]   Found role: arn:aws:iam::123456789012:role/lab_admin

[iam__enum_roles] Found 1 role(s):

[iam__enum_roles]     arn:aws:iam::123456789012:role/lab_admin

[iam__enum_roles] Checking to see if any of these roles can be assumed for temporary credentials...

[iam__enum_roles]   Role can be assumed, but hit max session time limit, reverting to minimum of 1 hour...

[iam__enum_roles]   Successfully assumed role for 1 hour: arn:aws:iam::123456789012:role/lab_admin

[iam__enum_roles] {
  "Credentials": {
    "AccessKeyId": "ASIAQOMAIGYUWZXRMMO2",
    "SecretAccessKey": "2UU80dtizqx3DUa9mn6033AjXKb13GXOMCy+tOUt",
    "SessionToken": "FwoGZXIvYXdzEO///////////wEaDCv5...",
    "Expiration": "2023-08-18 22:07:49+00:00"
  },
  "AssumedRoleUser": {
    "AssumedRoleId": "AROAQOMAIGYUR5KMGWT7V:dCkQ0O1y6n9KSQmGBaKJ",
    "Arn": "arn:aws:sts::123456789012:assumed-role/lab_admin/dCkQ0O1y6n9KSQmGBaKJ"
  }
}
Cleaning up the PacuIamEnumRoles-XbsIV role.
```

{% endcode %}

Now that we were able to assume the role, let's set environment variable and query the environment:

<pre class="language-bash"><code class="lang-bash">kali@kali:~$ export AWS_ACCESS_KEY=ASIAQOMAIGYUWZXRMMO2
kali@kali:~$ export AWS_SECRET_ACCESS_KEY=2UU80dtizqx3DUa9mn6033AjXKb13GXOMCy+tOUt
<strong>kali@kali:~$ export AWS_SESSION_TOKEN=FwoGZXIvYXdzEO///////////wEaDCv5...
</strong><strong>
</strong><strong># We don't need to include profile anymore as it will use our environment variables.
</strong>kali@kali:~$ aws ec2 describe-vpcs --region us-east-1
</code></pre>

## Initial IAM Reconnaissance

### Accessing the Lab

* Credentials for access as the *target user*.
  * Target ACCESS KEY ID
  * Target SECRET ACCESS KEY
* Credentials for access as the *challenge user*.
  * Challenge ACCESS KEY ID
  * Challenge SECRET ACCESS KEY
* Credentials for access as the *monitor user*.
  * Management Console login URL
  * Username
  * Password

### Examining Compromised Credentials

{% hint style="info" %}
The *get-caller-identity* subcommand is a good way to identify the account and identity of the credentials, and this action will never return an *AccessDenied* error. However, we should be aware that this action is logged in Cloudtrail's event history. As defenders, we should establish alerts for these types of calls as they are typically executed by attackers once they've compromised credentials.
{% endhint %}

Getting details about our compromised account:

```bash
kali@kali:~$ aws --profile target sts get-caller-identity
```

The more stealthy command to gather information, this results in logs of our attacker's account rather than the target's:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile challenge sts get-access-key-info --access-key-id AKIAQOMAIGYUVEHJ7WXM
```

{% endcode %}

Getting information in a stealthy way from error messages via a lambda function:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target lambda invoke --function-name arn:aws:lambda:us-east-1:123456789012:function:nonexistent-function outfile

An error occurred (AccessDeniedException) when calling the Invoke operation: User: arn:aws:iam::123456789012:user/support/clouddesk-plove is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:123456789012:function:nonexistent-function because no resource-based policy allows the lambda:InvokeFunction action
```

{% endcode %}

### Scoping IAM permissions

Listing inline policies:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam list-user-policies --user-name clouddesk-plove

{
    "PolicyNames": []
}
```

{% endcode %}

Listing policies associated with the user specified:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam list-attached-user-policies --user-name clouddesk-plove
{
    {
    "AttachedPolicies": [
        {
            "PolicyName": "deny_challenges_access",
            "PolicyArn": "arn:aws:iam::123456789012:policy/deny_challenges_access"
        }
    ]
}
}
```

{% endcode %}

Listing groups that the specified user is attached to:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam list-groups-for-user --user-name clouddesk-plove

{
    "Groups": [
        {
            "Path": "/support/",
            "GroupName": "support",
            "GroupId": "AGPAQOMAIGYUSHSVDSYIP",
            "Arn": "arn:aws:iam::123456789012:group/support/support",
        }
    ]
}
```

{% endcode %}

Checking inline and managed policies for the group discovered:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam list-group-policies --group-name support

{
    "PolicyNames": []
}

kali@kali:~$ aws --profile target iam list-attached-group-policies --group-name support
{
    "AttachedPolicies": [
        {
            "PolicyName": "SupportUser",
            "PolicyArn": "arn:aws:iam::aws:policy/job-function/SupportUser"
        }
    ]
}
```

{% endcode %}

{% hint style="warning" %}
While *AWS Managed Policies* offer flexibility for IAM management, they tend to be overly-permissive and there is an inherent security risk when they are used alone.
{% endhint %}

Determining the version of the AWS managed policy applied to the *support* group:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam list-policy-versions --policy-arn "arn:aws:iam::aws:policy/job-function/SupportUser"

{
    "Versions": [
        {
            "VersionId": "v8",
            "IsDefaultVersion": true
        },
        {
            "VersionId": "v7",
            "IsDefaultVersion": false,
        },
...
```

{% endcode %}

Retrieving the most recent version document:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-policy-version --policy-arn arn:aws:iam::aws:policy/job-function/SupportUser --version-id v8

{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": [
                        "support:*",
                        "acm:DescribeCertificate",
                        "acm:GetCertificate",
                        "acm:List*",
                        "acm-pca:DescribeCertificateAuthority",
                        "autoscaling:Describe*",
...
                        "workdocs:Describe*",
                        "workmail:Describe*",
                        "workmail:Get*",
                        "workspaces:Describe*"
                    ],
                    "Effect": "Allow",
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v8",
        "IsDefaultVersion": true,
...
    }
}
```

{% endcode %}

## IAM Resources Enumeration

### Choosing Between a Manual or Automated Enumeration Approach

### Enumerating IAM Resources

<table><thead><tr><th width="163">Resource Type</th><th width="159">Name</th><th>ARN</th></tr></thead><tbody><tr><td>IAM::User</td><td>clouddesk-plove</td><td>arn:aws:iam::123456789012:user/support/clouddesk-plove</td></tr><tr><td>IAM:Group</td><td>support</td><td>arn:aws:iam::123456789012:group/support/support</td></tr><tr><td>IAM::Policy</td><td>SupportUser</td><td>arn:aws:iam::aws:policy/job-function/SupportUser</td></tr></tbody></table>

Checking the actions our policy grants us the ability to us:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-policy-version --policy-arn arn:aws:iam::aws:policy/job-function/SupportUser --version-id v8 | grep "iam"

                        "iam:GenerateCredentialReport",
                        "iam:GenerateServiceLastAccessedDetails",
                        "iam:Get*",
                        "iam:List*",
```

{% endcode %}

Filter command via the aws help to show what all we can do:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam help | grep -E "list-|get-|generate-"

       o generate-credential-report
       o generate-organizations-access-report
       o generate-service-last-accessed-details
       o get-access-key-last-used
       o get-account-authorization-details
       o get-account-password-policy
       o get-account-summary
       o get-context-keys-for-custom-policy
       o get-context-keys-for-principal-policy
       o get-credential-report
       o get-group
       o get-group-policy
       o get-instance-profile
       o get-login-profile
       o get-open-id-connect-provider
       o get-organizations-access-report
       o get-policy
       o get-policy-version
       o get-role
       o get-role-policy
       o get-saml-provider
       o get-server-certificate
       o get-service-last-accessed-details
       o get-service-last-accessed-details-with-entities
       o get-service-linked-role-deletion-status
       o get-ssh-public-key
       o get-user
       o get-user-policy
       o list-access-keys
       o list-account-aliases
       o list-attached-group-policies
       o list-attached-role-policies
       o list-attached-user-policies
       o list-entities-for-policy
       o list-group-policies
       o list-groups
       o list-groups-for-user
       o list-instance-profile-tags
       o list-instance-profiles
       o list-instance-profiles-for-role
       o list-mfa-device-tags
       o list-mfa-devices
       o list-open-id-connect-provider-tags
       o list-open-id-connect-providers
       o list-policies
       o list-policies-granting-service-access
       o list-policy-tags
       o list-policy-versions
       o list-role-policies
       o list-role-tags
       o list-roles
       o list-saml-provider-tags
       o list-saml-providers
       o list-server-certificate-tags
       o list-server-certificates
       o list-service-specific-credentials
       o list-signing-certificates
       o list-ssh-public-keys
       o list-user-policies
       o list-user-tags
       o list-users
       o list-virtual-mfa-devices
```

{% endcode %}

Getting a summary of the IAM-related information in the account, tee'ing it to avoid interacting with the AWS API again:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-summary | tee account-summary.json

aws --profile target iam get-account-summary
{
    "SummaryMap": {
        "GroupPolicySizeQuota": 5120,
        "InstanceProfilesQuota": 1000,
        "Policies": 8,
        "GroupsPerUserQuota": 10,
        "InstanceProfiles": 0,
        "AttachedPoliciesPerUserQuota": 10,
        "Users": 18,
        "PoliciesQuota": 1500,
        "Providers": 1,
        "AccountMFAEnabled": 0,
        "AccessKeysPerUserQuota": 2,
        "AssumeRolePolicySizeQuota": 2048,
        "PolicyVersionsInUseQuota": 10000,
        "GlobalEndpointTokenVersion": 1,
        "VersionsPerPolicyQuota": 5,
        "AttachedPoliciesPerGroupQuota": 10,
        "PolicySizeQuota": 6144,
        "Groups": 8,
        "AccountSigningCertificatesPresent": 0,
        "UsersQuota": 5000,
        "ServerCertificatesQuota": 20,
        "MFADevices": 0,
        "UserPolicySizeQuota": 2048,
        "PolicyVersionsInUse": 27,
        "ServerCertificates": 0,
        "Roles": 20,
        "RolesQuota": 1000,
        "SigningCertificatesPerUserQuota": 2,
        "MFADevicesInUse": 0,
        "RolePolicySizeQuota": 10240,
        "AttachedPoliciesPerRoleQuota": 10,
        "AccountAccessKeysPresent": 0,
        "GroupsQuota": 300
    }
}
```

{% endcode %}

Enumerating all the IAM identities:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam list-users | tee users.json

{
    "Users": [
        {
            "Path": "/admin/",
            "UserName": "admin-alice",
            "UserId": "AIDAQOMAIGYU3FWX3JOFP",
            "Arn": "arn:aws:iam::123456789012:user/admin/admin-alice",
        },
...


kali@kali:~$ aws --profile target iam list-groups | tee groups.json

{
    "Groups": [
        {
            "Path": "/admin/",
            "GroupName": "admin",
            "GroupId": "AGPAQOMAIGYUXBR7QGLLN",
            "Arn": "arn:aws:iam::123456789012:group/admin/admin",
        },
...


kali@kali:~$ aws --profile target iam list-roles | tee roles.json

{
    "Roles": [
        {
            "Path": "/",
            "RoleName": "aws-controltower-AdministratorExecutionRole",
            "RoleId": "AROAQOMAIGYU6PUFJYD7W",
            "Arn": "arn:aws:iam::123456789012:role/aws-controltower-AdministratorExecutionRole",
...
```

{% endcode %}

Listing policies attached to IAM identities, only displaying the *Customer Managed Policies* and omitting the *AWS Managed Policies*:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam list-policies --scope Local --only-attached | tee policies.json

{
    "Policies": [
        {
            "PolicyName": "manage-credentials",
            "PolicyId": "ANPAQOMAIGYU3LK3BHLGL",
            "Arn": "arn:aws:iam::123456789012:policy/manage-credentials",
            "Path": "/",
            "DefaultVersionId": "v1",
            "AttachmentCount": 1,
            "PermissionsBoundaryUsageCount": 0,
            "IsAttachable": true,
            "UpdateDate": "2023-10-19T15:45:59+00:00"
        },
...
```

{% endcode %}

Getting the inline policies for every identity:

* **list-user-policies**
* **get-user-policy**
* **list-group-policies**
* **get-group-policy**
* **list-role-policies**
* **get-role-policy**

Checking for all managed policies:

* **list-attached-user-policies**
* **list-attached-group-policies**
* **list-attached-role-policies**

Then we can run the **get-policy-version** subcommand to read the policy document for each managed policy found.

Alternatively, if we have **Get\*** permissions, we can just run **get-account-authorization-details** to gather the same information of all the previous commands.

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User Group LocalManagedPolicy Role | tee account-authorization-details.json

{
    "UserDetailList": [
        {
            "Path": "/admin/",
            "UserName": "admin-alice",
            "UserId": "AIDAQOMAIGYU3FWX3JOFP",
            "Arn": "arn:aws:iam::123456789012:user/admin/admin-alice",
            "GroupList": [
                "amethyst_admin",
                "admin"
            ],
    ...
    "GroupDetailList": [
        {
            "Path": "/admin/",
            "GroupName": "admin",
            "GroupId": "AGPAQOMAIGYUXBR7QGLLN",
            "Arn": "arn:aws:iam::123456789012:group/admin/admin",
            "GroupPolicyList": [],
    ...
    "RoleDetailList": [
        {
            "Path": "/",
            "RoleName": "aws-controltower-AdministratorExecutionRole",
            "RoleId": "AROAQOMAIGYU6PUFJYD7W",
            "Arn": "arn:aws:iam::123456789012:role/aws-controltower-AdministratorExecutionRole",
    ...
    "Policies": [
        {
            "PolicyName": "ruby_admin",
            "PolicyId": "ANPAQOMAIGYU3I3WDCID3",
            "Arn": "arn:aws:iam::123456789012:policy/ruby/ruby_admin",
            "Path": "/ruby/",
...
```

{% endcode %}

### Processing API Response data with JMESPath

Example querying all user information via **get-account-authorization-details**:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User

{
    "UserDetailList": [
        {
            "Path": "/admin/",
            "UserName": "admin-alice",
            "UserId": "AIDAQOMAIGYUSSOCFCREC",
            "Arn": "arn:aws:iam::123456789012:user/admin/admin-alice",
            "GroupList": [
                "admin"
            ],
            "AttachedManagedPolicies": [],
            "Tags": []
        },
        {
            "Path": "/amethyst/",
            "UserName": "admin-cbarton",
            "UserId": "AIDAQOMAIGYUTHT4D5YLG",
            "Arn": "arn:aws:iam::123456789012:user/amethyst/admin-cbarton",
            "GroupList": [
                "amethyst_admin"
            ],
            "AttachedManagedPolicies": [],
            "Tags": []
        },
...
```

{% endcode %}

Now using JMESPath expression to query just the UserName field:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[].UserName"

[
    "admin-alice",
    "admin-cbarton",
    "admin-srogers",
    "admin-tstark",
    "clouddesk-bob",
...
```

{% endcode %}

Additional examples:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[0].[UserName,Path,GroupList]"

[
    "admin-alice",
    "/admin/",
    [
        "admin"
    ]
]


kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[0].{Name: UserName,Path: Path,Groups: GroupList}"

{
    "Name": "admin-alice",
    "Path": "/admin/",
    "Groups": [
        "admin"
    ]
}
```

{% endcode %}

Listing all *IAM Users* with a username containing the word 'admin' via *Filter Projections*:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[?contains(UserName, 'admin')].{Name: UserName}"

[
    {
        "Name": "admin-alice"
    },
    {
        "Name": "admin-cbarton"
    },
    {
        "Name": "admin-srogers"
    },

...
```

{% endcode %}

More examples with Filter Projections:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User Group --query "{Users: UserDetailList[?Path=='/admin/'].UserName, Groups: GroupDetailList[?Path=='/admin/'].{Name: GroupName}}"

{
   "Users": [
       "admin-alice"
   ],
   "Groups": [
       {
           "Name": "admin"
       }
   ]
}
```

{% endcode %}

### Running Automated Enumeration with Pacu

Similar to **get-account-authorization-details** w/ pacu:

{% code overflow="wrap" %}

```bash
Pacu (enumlab:imported-target) > run iam__enum_users_roles_policies_groups
  Running module iam__enum_users_roles_policies_groups...
[iam__enum_users_roles_policies_groups] Found 18 users
[iam__enum_users_roles_policies_groups] Found 20 roles
[iam__enum_users_roles_policies_groups] Found 8 policies
[iam__enum_users_roles_policies_groups] Found 8 groups
[iam__enum_users_roles_policies_groups] iam__enum_users_roles_policies_groups completed.

[iam__enum_users_roles_policies_groups] MODULE SUMMARY:

  18 Users Enumerated
  20 Roles Enumerated
  8 Policies Enumerated
  8 Groups Enumerated
  IAM resources saved in Pacu database.
```

{% endcode %}

Listing data in the Pacu database:

{% code overflow="wrap" %}

```bash
Pacu (enumlab:imported-target) > services
  IAM

Pacu (enumlab:imported-target) > data IAM
{
  "Groups": [
    {
      "Arn": "arn:aws:iam::123456789012:group/admin/admin",
      "GroupId": "AGPAQOMAIGYUZQMC6G5NM",
      "GroupName": "admin",
      "Path": "/admin/"
    },
    {
      "Arn": "arn:aws:iam::123456789012:group/amethyst/amethyst_admin",
      "GroupId": "AGPAQOMAIGYUYF3JD3FXV",
      "GroupName": "amethyst_admin",
      "Path": "/amethyst/"
    },
...
```

{% endcode %}

### Extracting Insights from Enumeration Data

Analyzing *admin-alice*'s IAM user data:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User Group --query "UserDetailList[?UserName=='admin-alice']"

[
    {
        "Path": "/admin/",
        "UserName": "admin-alice",
        "UserId": "AIDAQOMAIGYU3FWX3JOFP",
        "Arn": "arn:aws:iam::123456789012:user/admin/admin-alice",
        "GroupList": [
            "amethyst_admin",
            "admin"
        ],
        "AttachedManagedPolicies": [],
        "Tags": [
            {
                "Key": "Project",
                "Value": "amethyst"
            }
        ]
    }
]
```

{% endcode %}

Querying the information about the two groups the user belongs to:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User Group --query "GroupDetailList[?GroupName=='admin']"

[
    {
        "Path": "/admin/",
        "GroupName": "admin",
        "GroupId": "AGPAQOMAIGYUXBR7QGLLN",
        "Arn": "arn:aws:iam::123456789012:group/admin/admin",
        "GroupPolicyList": [],
        "AttachedManagedPolicies": [
            {
                "PolicyName": "AdministratorAccess",
                "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
            }
        ]
    }
]


kali@kali:~$ aws --profile target iam get-account-authorization-details --filter User Group --query "GroupDetailList[?GroupName=='amethyst_admin']"

[
    {
        "Path": "/amethyst/",
        "GroupName": "amethyst_admin",
        "GroupId": "AGPAQOMAIGYUX23CDL3AN",
        "Arn": "arn:aws:iam::123456789012:group/amethyst/amethyst_admin",
        "GroupPolicyList": [],
        "AttachedManagedPolicies": [
            {
                "PolicyName": "amethyst_admin",
                "PolicyArn": "arn:aws:iam::123456789012:policy/amethyst/amethyst_admin"
            }
        ]
    }
]
```

{% endcode %}

Paths to go from here:

1. Social engineer Alice's credentials -- we know there is no MFA.
2. Searching for credentials with permission to modify *admin-alice*'s credentials.&#x20;
   1. In this case, we could look for policies with *iam:"\*",* or *iam:CreateAccessKey* allowed actions.
3. Obtaining credentials of another *admin* user or find a user with permissions to add users to the *admin* group.
   1. In this case we'd be looking for policies with *iam:"\*"*, or *iam:AddUserToGroup* allowed actions.

Displaying policies associated with the *amethyst\_admin* group:

{% code overflow="wrap" %}

```bash
kali@kali:~$ aws --profile target iam get-account-authorization-details --filter LocalManagedPolicy --query "Policies[?PolicyName=='amethyst_admin']"

[
    {
        "PolicyName": "amethyst_admin",
        "PolicyId": "ANPAQOMAIGYUUA3PZUK57",
        "Arn": "arn:aws:iam::123456789012:policy/amethyst/amethyst_admin",
        "Path": "/amethyst/",
        "DefaultVersionId": "v7",
        "AttachmentCount": 1,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "PolicyVersionList": [
            {
                "Document": {
                    "Statement": [
                        {
                            "Action": "iam:*",
                            "Effect": "Allow",
                            "Resource": [
                                "arn:aws:iam::123456789012:user/amethyst/*",
                                "arn:aws:iam::123456789012:group/amethyst/*",
                                "arn:aws:iam::123456789012:role/amethyst/*",
                                "arn:aws:iam::123456789012:policy/amethyst/*"
                            ],
                            "Sid": "AllowAllIAMActionsInUserPath"
                        },
                        {
                            "Action": "iam:*",
                            "Condition": {
                                "StringEquals": {
                                    "aws:ResourceTag/Project": "amethyst"
                                }
                            },
                            "Effect": "Allow",
                            "Resource": "arn:aws:iam::*:user/*",
                            "Sid": "AllowAllIAMActionsInGroupMembers"
                        },
                        {
                            "Action": [
                                "ec2:*",
                                "lambda:*"
                            ],
                            "Condition": {
                                "StringEquals": {
                                    "aws:ResourceTag/Project": "amethyst"
                                }
                            },
                            "Effect": "Allow",
                            "Resource": "*",
                            "Sid": "AllowAllActionsInTaggedResources"
                        },
                        {
                            "Action": [
                                "ec2:*",
                                "lambda:*"
                            ],
                            "Condition": {
                                "StringEquals": {
                                    "aws:RequestTag/Project": "amethyst"
                                }
                            },
                            "Effect": "Allow",
                            "Resource": "*",
                            "Sid": "AllowAllActionsInTaggedResources2"
                        },
                        {
                            "Action": "s3:*",
                            "Effect": "Allow",
                            "Resource": [
                                "arn:aws:s3:::amethyst*",
                                "arn:aws:s3:::amethyst*/*"
                            ],
                            "Sid": "AllowAllS3ActionsInPath"
                        }
                    ],
                    "Version": "2012-10-17"
                },
                "IsDefaultVersion": true,
            },
    }
]
```

{% endcode %}

Userful search listing Users, groups, and attached managed policies:

{% code overflow="wrap" %}

```bash
aws --profile target iam get-account-authorization-details --filter User Group --query "UserDetailList[].{Name: UserName,Path: Path,Groups: GroupList, AttachedManagedPolicies:AttachedManagedPolicies}"
```

{% endcode %}
