WDavid404 / OSCP

0 stars 0 forks source link

24. Enumerating AWS Cloud Infrastructure #32

Open WDavid404 opened 2 weeks ago

WDavid404 commented 2 weeks ago

24.2. Reconnaissance of Cloud Resources on the Internet

Goal: to find publicly-accessible resources as well as resources that weren't meant to be publicly-accessible (typically due to misconfigurations).

Modifying /etc/resolv.conf to Add a New Nameserver image image

Note: 
Even if you become a root user, you can't do it, right? It may be set to immutable mode. let do it!
chattr -i /etc/resolv.conf

chattrコマンドで、変更不可等のファイル属性を変更することができます。「+属性」で属性の設定、「-属性」で属性の削除を行う

Note: better to use firefox browser. found that Chromium browser didn't work with our addede nameserver.

24.2.2. Domain and Subdomain Reconnaissance

  1. use the host command
    1. run whois command image image image image

Use auto tool: dnsenum offseclab.io --threads 100 image

24.2.3. Service-specific Domains

Using browser developer tools on network tab, we can know some images were from s3.amazonaws.com e.g. From the path offseclab-assets-public-axevtewi/sites/www/images/amethyst.png, we can learn that the S3 bucket name is offseclab-assets-public-axevtewi and the object key is sites/www/images/ruby-expanded.png.

We'll remove the object key from the URL like the following: http://domain/bucket_name, then browse to that URL. --> if misconfiguration in the bucket , we can get some info that not intended for public.

Refer: Custom URLs of All The Three Major CSPs image

Auto tool: cloud_enum: e.g. run a quickscan for the bucket named "offseclab-assets-private-axevtewi" cloud_enum -k offseclab-assets-public-axevtewi --quickscan --disable-azure --disable-gcp

Extend key for other env.

## bash to make a key list for different envs
for key in "public" "private" "dev" "prod" "development" "production"; do echo "offseclab-assets-$key-axevtewi"; done | tee /tmp/keyfile.txt

## scan by using the generated key list
cloud_enum -kf /tmp/keyfile.txt -qs --disable-azure --disable-gcp

Q3

cloud_enum -k offseclab-ruby-jroupdcc -qs --disable-azure --disable-gcp --> image http://offseclab-ruby-jroupdcc.s3.amazonaws.com/proof.txt --> The proof was moved to the new 'Greem Gemstone' project. --> "Greem" should be typo for "Green". Google for "green gemstone names": image so, cloud_enum -k offseclab-Emerald-jroupdcc -qs --disable-azure --disable-gcp --> image

Visiting http://offseclab-emerald-jroupdcc.s3.amazonaws.com/ --> Access denied Visiting http://offseclab-emerald-jroupdcc.s3.amazonaws.com/proof.txt --> Got it

WDavid404 commented 2 weeks ago

24.3. Reconnaissance via Cloud Service Provider's API

24.3.1. Preparing the Lab - Configure AWS CLI

### install awscli
sudo apt update
sudo apt install -y awscli

### To use the profile, we'll need to add the --profile attacker argument to every AWS command we run.
aws configure --profile attacker

###  Let's test this by running the aws --profile attacker sts get-caller-identity command.
aws --profile attacker sts get-caller-identity

image

aws configure list-profiles image

aws configure --profile attacker list image

24.3.2. Publicly Shared Resources

We'll focus on the following commonly used resources:

### "ec2 describe-images" will list all the images that the account can read. The command below is to shows a list of public AMIs owned by Amazon.
aws --profile attacker ec2 describe-images --owners amazon --executable-users all

### use filter
aws --profile attacker ec2 describe-images --executable-users all --filters "Name=description,Values=*Offseclab*"
aws --profile attacker ec2 describe-images --executable-users all --filters "Name=name,Values=*Offseclab*"
aws --profile attacker ec2 describe-snapshots --filters "Name=description,Values=*offseclab*"

Q3:

aws --profile attacker ec2 describe-snapshots --filters "Name=volume-size,Values=1" --owner-ids self

24.3.3. Obtaining Account IDs from S3 Buckets

How we can abuse the API features and capabilities to obtain the target's account ID from a publicly-shared S3 bucket or object? example: image image image

To list bucket name curl -s www.offseclab.io | grep -o -P 'offseclab-assets-public-\w{8}' image

aws --profile attacker s3 ls offseclab-assets-public-owqpisxp image

create an IAM

aws --profile attacker iam create-user --user-name enum
aws --profile attacker iam create-access-key --user-name enum

image

aws configure --profile enum image

aws sts get-caller-identity --profile enum image

Getting AccountID from a Public S3 Bucket or Object. image

Write a policy that will allow for listing the content of the bucket and reading objects inside it.

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

attach this policy to enum user

aws --profile attacker iam put-user-policy \
--user-name enum \
--policy-name s3-read \
--policy-document file://policy-s3-read.json

check it aws --profile attacker iam list-user-policies --user-name enum image

⬆️ According to the policy we set, the user will be able to read the content of the bucket only if the account ID where the bucket resides starts with "0". In our lab, our account is "123456789012". It doesn't start with "0", so we'll get an AccessDenied error when trying to list the bucket. If we change the policy in the file and apply it again to the enum user, we'll be able to list the bucket. This time it works because our account starts with the digit "1". Once we know that the policy starts with a digit, we can move to the next one by modifying the condition of the policy.

24.3.4. Enumerating IAM Users in Other Accounts

To enumerates internal IAM identities when we know the AWS account ID of the target.

Typically, we use the AWS Resource Name (ARN) to specify an IAM identity, as shown below:

"Principal": {
  "AWS": ["arn:aws:iam::AccountID:user/user-name"]
}

e.g.
"Principal": {
  "AWS": ["arn:aws:iam::123456789012:user/cloudadmin"]
}

Step1. Create an S3 bucket inside our attacker's account: aws s3 mb s3://offseclab-dummy-bucket-$RANDOM-$RANDOM-$RANDOM will create a bucket with the name offseclab-dummy-bucket By default, the newly-created bucket is private. Now we are going to define a policy document in which we'll grant read permission only to a specific IAM user in the target account.

{
    "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"

        }
    ]
}

### attach policy to the s3 bucket created (must use the prefix file:// to instruct AWS CLI to read the policy from that file.)
`aws --profile attacker s3api put-bucket-policy --bucket offseclab-dummy-bucket-28967-25641-13328 --policy file://grant-s3-bucket-read.json`
--> If no error returns, it means that the "cloudadmin" user exists in the target account.

###  copy the policy to create a new one - but this time, we'll grant privileges to a nonexistent principal.
cp grant-s3-bucket-read.json grant-s3-bucket-read-userDoNotExist.json
### edit it: cat grant-s3-bucket-read-userDoNotExist.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/nonexistant"]
            },
            "Action": "s3:ListBucket"

        }
    ]
}

aws --profile attacker s3api put-bucket-policy --bucket offseclab-dummy-bucket-28967-25641-13328  --policy file://grant-s3-bucket-read-userDoNotExist.json
--> it returns: An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: Invalid principal in policy --> The username "nonexistant" didn't exit.

Have a list of potential role name for brute forcing

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

Step2 Use a popular tool named pacu that can automate this technique of user and role enumeration sudo apt install pacu we'll run pacu without any other argument to start it in interactive mode. image

### import the attacker profile
Pacu (offseclab:No Keys Set) > import_keys attacker
  Imported keys as "imported-attacker"
Pacu (offseclab:imported-attacker) > 

### Listing Modules in pacu
Pacu (offseclab:imported-attacker) > ls
...
[Category: RECON_UNAUTH]

  iam__enum_roles
  iam__enum_users
...

### Running the enum_roles Module in pacu
Pacu (offseclab:imported-attacker) > run iam__enum_roles --word-list /tmp/role-names.txt --account-id 123456789012
WDavid404 commented 1 week ago

24.4. Initial IAM Reconnaissance

24.4.2. Examining Compromised Credentials

  1. One way to do this is with the aws sts get-caller-identity command, which will provide us with two pieces of information about the IAM identity whose credentials are used to call the operation. These include the account ID, the identity type (IAM user or role) and the name of the identity. aws --profile target sts get-caller-identity 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.]

aws --profile challenge sts get-access-key-info --access-key-id AKIAQOMAIGYUVEHJ7WXM Executing this command from an external account ensures that the event logs within the attacker's account instead of the target's.

Another stealthy approach is to abuse error messages that aren't logged by default in the Cloudtrail event history. For example, let's try invoking a nonexistent Lambda function using the compromised credentials. In general, we can execute a function with aws lambda invoke --function-name aws --profile target lambda invoke --function-name arn:aws:lambda:us-east-1:123456789012:function:nonexistent-function outfile image the error message offers us valuable information, including the account ID, the type of identity (IAM user or role), and the name of the identity executing this operation.

There isn't a "list" of actions that aren't logged in the Cloudtrail event history. However, Cloudtrail's Documentation states that data events and insights events are not displayed. Since invoking a Lambda function is considered a data event, it is not displayed in the event history. It's important to note that while these events might not be logged by default in the event history, they can indeed be captured (if configured) using trails.

One final consideration is that attackers may attempt to discern which AWS regions the account operates in and execute these commands in a different region. This increases the likelihood of evading detection.

24.4.3. Scoping IAM permissions

kali@kali:~$ aws --profile target sts get-caller-identity
{
    "UserId": "AIDAQOMAIGYUYNMOIF46I",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/support/clouddesk-plove"
}

## To list inline policies and managed policies associated with the user
aws --profile target iam list-user-policies --user-name clouddesk-plove
-->
{
    "PolicyNames": []
}

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"
        }
    ]
}
}

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",   
        }
    ]
}
!We discovered that the IAM user belongs to only one group, named support.

### Next, we'll check for policies associated with the support group. 
aws --profile target iam list-group-policies --group-name support
aws --profile target iam list-attached-group-policies --group-name support
--> 
{
    "AttachedPolicies": [
        {
            "PolicyName": "SupportUser",
            "PolicyArn": "arn:aws:iam::aws:policy/job-function/SupportUser"
        }
    ]
}

From the output, we learn that the group doesn't have any inline policy, but it does have an attached managed policy. This particular policy is classified as an AWS Managed Policy, a special set of policies provided by AWS with pre-defined permissions to quickly attach.
From the output, we learn that the group doesn't have any inline policy, but it does have an attached managed policy. This particular policy is classified as an [AWS Managed Policy](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/about-managed-policy-reference.html), a special set of policies provided by AWS with pre-defined permissions to quickly attach

### confirm the policy version
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,
        },
...

### To retrieve the policy document with aws iam get-policy-version.
aws --profile target iam get-policy-version --policy-arn arn:aws:iam::aws:policy/job-function/SupportUser --version-id v8

Next, We can build a script to automate this brute-force approach or leverage popular AWS tools such as pacu (Module: iam__bruteforce_permissions), awsenum or enumerate-iam. We can use any of these approaches to discover permissions in compromised credentials. Just like many brute-force attacks, these tools will generate a lot of ActionDenied account events that could trigger alarms within the target. This may not be a problem in some pentesting assessments which do not require stealth. In red-teaming assessments which prioritize stealth, we could adopt a manual approach, which will generate fewer errors and sound less alarms. We should always leverage information from our reconnaissance to help with this. For example, if we determined a list of services the account is using, we can target those services first, reducing the amount of noise we generate.

Q3

Using pacu "run ec2__enum", next, image --> can find proof info

WDavid404 commented 1 week ago

24.5. IAM Resources Enumeration

24.5.1. Choosing Between a Manual or Automated Enumeration Approach

Most tools generate significant log events and may trigger monitoring systems. This may not be a significant consideration when performing a red team assessment or a penetration test in which stealth is not a requirement, but when stealth is a factor, we must test our tools to determine the potential impact prior to an engagement.

24.5.2. Enumerating IAM Resources

### Let's check what actions this policy grants to enumerate IAM resources.
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*",

With this policy, we can run any iam subcommand that starts with get and list and two other specific actions. 
### We can use help
aws --profile target iam help | grep -E "list-|get-|generate-"

### Let's start by getting a summary of the IAM-related information in the account.
aws --profile target iam get-account-summary | tee account-summary.json
--->

### from the output, we can know users belong to which groups
...
{
            "Path": "/ruby/",
            "UserName": "dev-ballen",
            "UserId": "AIDASIAI6XMHOX2ZRNAOE",
            "Arn": "arn:aws:iam::154637613838:user/ruby/dev-ballen",
            "CreateDate": "2024-09-03T01:51:26+00:00",
            "GroupList": [
                "ruby_dev"
            ],
            "AttachedManagedPolicies": [],
            "Tags": [
                {
                    "Key": "ce1df3c0-33f8-4eac-bb8a-356a133b3ac0",
                    "Value": "ce1df3c0-33f8-4eac-bb8a-356a133b3ac0"
                },
                {
                    "Key": "Project",
                    "Value": "ruby"
                }
            ]
        },
.....

### list users, groups,roles
aws --profile target iam list-users | tee  users.json
aws --profile target iam list-groups | tee groups.json
aws --profile target iam list-roles | tee roles.json

### list all managed polices
aws --profile target iam list-policies --scope Local --only-attached | tee policies.json

Next, to get the inline policies for every identity associated with the compromised credentials, we could run the following subcommands:
- list-user-policies
- get-user-policy
- list-group-policies
- get-group-policy
- list-role-policies
- get-role-policy

Similarly, we can check for all managed policies with the following subcommands:

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

- get-policy-version

### get-account-authorization-details (To run it,the account running the command must have the GetAccountAuthorizationDetails permission attached to its policy)

aws --profile target iam get-account-authorization-details --filter User Group LocalManagedPolicy Role | tee account-authorization-details.json

aws --profile target iam list-attached-user-policies --user-name clouddesk-plove

aws --profile target iam list-policy-versions --policy-arn arn:aws:iam::12345678912:policy/deny_challenges_access

aws --profile target iam get-account-authorization-details --filter LocalManagedPolicy

24.5.3. Processing API Response data with JMESPath

JMESPath is a query language for JSON. It is used to extract and transform elements from a JSON document.

aws --profile target iam get-account-authorization-details --filter User image

aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[].UserName"

aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[0].[UserName,Path,GroupList]"

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

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

### What JMESPath expression will filter and display all users that contain the word "admin" in the Username and the Path fields? (Write only the JMESPath expression starting with "?".
     ?contains(UserName,'admin') && contains(Path,'admin')

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

24.5.4. Running Automated Enumeration with Pacu

run iam__enum_users_roles_policies_groups Next, we'll run the services command. Then we can run the data command to display all data for the specified service in the session.

swap_keys: let us change the currently active AWS key to another key that has previously been set for this session

24.5.5. Extracting Insights from Enumeration Data

### Getting the "admin-alice" IAM user details
aws --profile target iam get-account-authorization-details --filter User Group --query "UserDetailList[?UserName=='admin-alice']"

### check groups
aws --profile target iam get-account-authorization-details --filter User Group --query "GroupDetailList[?GroupName=='admin']"

### check policies
aws --profile target iam get-account-authorization-details --filter LocalManagedPolicy --query "Policies[?PolicyName=='amethyst_admin']"

image -----> Let's analyze the AllowAllIAMActionsInGroupMembers statement. This rule allows any IAM action to run on any resource that has a tag with a key-value pair equal to "Project:amethyst". This would prevent the administrator users of this project from modifying users from other projects.

For some reason, the admin-alice user, which is highly-privileged, is a member of the amethyst_admin group and the user is also tagged with the project name. The main problem is the tag, because any user who is a member of the amethyst_admin group can run dangerous actions like creating new access keys (iam:CreateAccessKey) for the admin-alice user, granting access with the privileges of this user. So, getting credentials from the admin-cbarton IAM user could lead to privilege escalation.

image The path reveals that the user admin-cbarton, positioned at the bottom-left, is a member of the amethyst_admin group and thereby inherits the amethyst_admin policy. At the top, we find admin-alice, a member of the admin group, linked to the AdministratorAccess policy, which makes this user an Effective Admin. The path indicates that the admin-cbarton user has permission to impersonate the higher-privileged admin-alice user.

Q3

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