Cheetsheet - AWS - Scenario - Utilise Public EBS snapshots

Overview

Access publicly accessible EBS snapshots to retrieve sensitive information.

Service/Tool: EBS, EC2, IAM, awcli
Use Case: Locate EBS snapshot that is publicly accessible for our target, copy it, then mount it to look for sensitive data.
Prerequisites: An AWS account ID


Attack Workflow

1. Step 1 (Discovery/Access)

Objective: Locate publicly accessible EBS snapshots using compromised credentials
Command/Method:

First we will enumerate our compromised account for the operations available on EBS to locate further information that may assist with our objection (i.e. data exfiltration/sensitive information discovery from EBS)

We configure AWSCLI with our compromised keys then run some basic whoami-esk commands:

aws sts get-caller-identity
{
    "UserId": "AIDARQVIRZ4UJNTLTYGWU",
    "Account": "104506445608",
    "Arn": "arn:aws:iam::104506445608:user/intern"
}

aws iam get-user

An error occurred (AccessDenied) when calling the GetUser operation: User: arn:aws:iam::104506445608:user/intern is not authorized to perform: iam:GetUser on resource: user intern because no identity-based policy allows the iam:GetUser action

Unfortunately we do not have permissions to run the aws iam get-user command however it does demonstrate that we can use the error message to find the username of our compromised account, intern.

We can then conduct some reconnaissance on this account using a handful of IAM commands.

First we will review the attached policies to determine the permissions of our account:

aws iam list-attached-user-policies --user-name intern
{
    "AttachedPolicies": [
        {
            "PolicyName": "PublicSnapper",
            "PolicyArn": "arn:aws:iam::104506445608:policy/PublicSnapper"
        }
    ]
}

Our compromised account has the policy "PublicSnapper" attached. Let's see what permissions this policy has. First we figure out the current version of our policy:

aws iam get-policy --policy-arn arn:aws:iam::104506445608:policy/PublicSnapper
{
    "Policy": {
        "PolicyName": "PublicSnapper",
        "PolicyId": "ANPARQVIRZ4UD6B2PNSLD",
        "Arn": "arn:aws:iam::104506445608:policy/PublicSnapper",
        "Path": "/",
        "DefaultVersionId": "v9",
        "AttachmentCount": 1,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "CreateDate": "2023-06-10T22:33:41+00:00",
        "UpdateDate": "2024-01-15T23:47:11+00:00",
        "Tags": []
    }
}

then we can view the contents of the policy:

aws iam get-policy-version --policy-arn arn:aws:iam::104506445608:policy/PublicSnapper --version-id v9
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "Intern1",
                    "Effect": "Allow",
                    "Action": "ec2:DescribeSnapshotAttribute",
                    "Resource": "arn:aws:ec2:us-east-1::snapshot/snap-0c0679098c7a4e636"
                },
                {
                    "Sid": "Intern2",
                    "Effect": "Allow",
                    "Action": "ec2:DescribeSnapshots",
                    "Resource": "*"
                },
                {
                    "Sid": "Intern3",
                    "Effect": "Allow",
                    "Action": [
                        "iam:GetPolicyVersion",
                        "iam:GetPolicy",
                        "iam:ListAttachedUserPolicies"
                    ],
                    "Resource": [
                        "arn:aws:iam::104506445608:user/intern",
                        "arn:aws:iam::104506445608:policy/PublicSnapper"
                    ]
                },
                {
                    "Sid": "Intern4",
                    "Effect": "Allow",
                    "Action": [
                        "ebs:ListSnapshotBlocks",
                        "ebs:GetSnapshotBlock"
                    ],
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v9",
        "IsDefaultVersion": true,
        "CreateDate": "2024-01-15T23:47:11+00:00"
    }
}
}

A couple of interesting notes from this output:

  • ec2:DescribeSnapshotAttribute on arn:aws:ec2:us-east-1::snapshot/snap-0c0679098c7a4e636. From the AWS documentation, this will allow us to query the attributes associated with the specified snapshot, where the attributes specify who can create volumes from the snapshot.
  • ec2:DescribeSnapshots on * which will allow us to list all EBS snapshots within an AWS account, or another AWS account should the snapshot be public!
  • ebs:ListSnapshotBlocks and ebs:GetSnapshotBlock on *. These API calls will allow us to list metadata about the blocks stored in a snapshot and get the blocks from EBS snapshots.

2. Step 2 (Escalation/Exploitation)

Objective: Using the compromised account, list available EBS snapshots
Command/Method:

We will first describe (list) all of the snapshots for compromised AWS account:

aws ec2 describe-snapshots --owner-ids self --region us-east-1
{
    "Snapshots": [
        {
            "Tags": [
                {
                    "Key": "Name",
                    "Value": "PublicSnapper"
                }
            ],
            "StorageTier": "standard",
            "TransferType": "standard",
            "CompletionTime": "2023-06-12T15:22:57.924000+00:00",
            "SnapshotId": "snap-0c0679098c7a4e636",
            "VolumeId": "vol-0ac1d3295a12e424b",
            "State": "completed",
            "StartTime": "2023-06-12T15:20:20.580000+00:00",
            "Progress": "100%",
            "OwnerId": "104506445608",
            "Description": "Created by CreateImage(i-06d9095368adfe177) for ami-07c95fb3e41cb227c",
            "VolumeSize": 8,
            "Encrypted": false
        },
        {
            "StorageTier": "standard",
            "TransferType": "standard",
            "CompletionTime": "2023-08-24T19:34:22.909000+00:00",
            "SnapshotId": "snap-035930ba8382ddb15",
            "VolumeId": "vol-09149587639d7b804",
            "State": "completed",
            "StartTime": "2023-08-24T19:30:49.742000+00:00",
            "Progress": "100%",
            "OwnerId": "104506445608",
            "Description": "Created by CreateImage(i-0199bf97fb9d996f1) for ami-0e411723434b23d13",
            "VolumeSize": 24,
            "Encrypted": false
        }
    ]
}

Next we would like to find the ones that are publicly accessible. To do this, we must enumerate the attributes of the snapshot, which we can do with the ec2:DescribeSnapshotAttribute. Recalling from step 1, we had this permission to the snapshot ID snap-0c0679098c7a4e636, so let's query that one:

aws ec2 describe-snapshot-attribute --attribute createVolumePermission --snapshot-id snap-0c0679098c7a4e636 --region us-east-1
{
    "SnapshotId": "snap-0c0679098c7a4e636",
    "CreateVolumePermissions": [
        {
            "Group": "all"
        }
    ]
}

Per the documentation this "Group":"all" within the CreateVolumePerimissions attribute indicates that users in all groups (i.e. anonymous users) can create a volume from this snapshot.

We can also enumerate public snapshots via the following command, which provides us with the same output - of particular note this command will work against any AWS account - that is, we can enumerate the publicly accessible EBS snapshots of any AWS account as long as we have the account ID!

aws ec2 describe-snapshots --owner-id self --restorable-by-user-ids all --no-paginate --region us-east-1

{
    "Snapshots": [
        {
            "Tags": [
                {
                    "Key": "Name",
                    "Value": "PublicSnapper"
                }
            ],
            "StorageTier": "standard",
            "TransferType": "standard",
            "CompletionTime": "2023-06-12T15:22:57.924000+00:00",
            "SnapshotId": "snap-0c0679098c7a4e636",
            "VolumeId": "vol-0ac1d3295a12e424b",
            "State": "completed",
            "StartTime": "2023-06-12T15:20:20.580000+00:00",
            "Progress": "100%",
            "OwnerId": "104506445608",
            "Description": "Created by CreateImage(i-06d9095368adfe177) for ami-07c95fb3e41cb227c",
            "VolumeSize": 8,
            "Encrypted": false
        }
    ]
}

3. Step 3 (Final Exploitation/Outcome)

Objective: Create a new volume from the publicly accessible snapshot.
Command/Method:

AWS has some pretty good documentation on this which can found at the following URL: https://docs.aws.amazon.com/prescriptive-guidance/latest/backup-recovery/restore.html


Detection and Defense

Indicators of Compromise (IoCs):

Via CloudTrail:

  • ModifySnapshotAttribute event when requestParameters.createVolumePermission shows the EBS snapshot was shared with an account ID that is unknown, or "groups":"all"
  • SharedSnapshotCopyInitiated / SharedSnapshotVolumeCreated is logged, along with the userIdentity.accountId that contains the attacker's account ID.

Mitigation Techniques:

Review and remove publicy accessible snapshots using the commands mentioned in the article. Any secrects contained within these snapshots should be rotated.


Notes and References

Links:
PwnedLabs - Loot public EBS snapshots
DataDog - Exfiltrate EBS Snapshot by Sharing it
AWS - DescribeSnapshotAttribute
AWS - CreateVolumePermission