Exploiting AWS 3 - Defender's Perspective (Flaws2.Cloud)
In this path as a defender, you'll investigate a successful attack by using jq & Athena to analyze CloudTrail logs in JSON.
Table of contents
This is a walkthrough of the flaws2.cloud challenge where you focus on AWS-specific issues, so no buffer overflows, XSS, etc. You can play by getting hands-on keyboard or just click through the hints to learn the concepts and go from one level to the next without playing.
Defender
In this Defender path, that target Exploiting AWS 2 - Attacker's Perspective (Flaws2.Cloud) is now viewed as the victim and you'll work as an Incident Responder for that same app, understanding how an attack happened.
You'll get access to logs of a previous successful attack and as the Defender, you'll learn the power of jq in analyzing logs, and instructions on how to set up Athena in your own environment.
Video Walkthrough:
Accessing the AWS Environment
Credentials
Your IAM credentials to the Security account:
Login: <https://flaws2-security.signin.aws.amazon.com/console>
Account ID: 322079859186
Username: security
Password: password
Access Key: AKIAIUFNQ2WCOPTEITJQ
Secret Key: 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
These credentials give you access to the Security account, which can assume the role “security” in the Target account.
Preventing key leakage by using AWS-Vault
I’ll be using aws-vault as it avoids storing the keys in plain text in your home directory as the AWS CLI does.
Installation
- First, install Homebrew on your Linux host
/bin/bash -c "$(curl -fsSL [https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh](https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh))"
- Add Homebrew to your PATH and to your bash shell profile
test -d ~/.linuxbrew && eval "$(~/.linuxbrew/bin/brew shellenv)"
test -d /home/linuxbrew/.linuxbrew && eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
test -r ~/.bash_profile && echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> ~/.bash_profile
echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> ~/.profile
- Install AWS Vault
brew install aws-vault
Usage
# Store AWS credentials for the "security" profile
└─$ aws-vault add security
Enter Access Key Id: ABDCDEFDASDASF
Enter Secret Key: %%%
# Execute a get-caller-identity command (using temporary credentials)
└─$ aws-vault exec security -- aws sts get-caller-identity
{
"UserId": "AIDAJXZBU42TNFRNGBBFI",
"Account": "322079859186",
"Arn": "arn:aws:iam::322079859186:user/security"
}
# open a browser window and log in to the AWS Console
└─$ aws-vault login security
# List credentials
└─$ aws-vault list
Profile Credentials Sessions
======= =========== ========
security security sts.GetSessionToken:54m42s
Objective 1
Download CloudTrail Logs
- Make a directory for the cloudtrail logs
┌──(kali㉿kali)-[~]
└─$ mkdir flaws2logs
- Download the CloudTrail Logs into the new directory
┌──(kali㉿kali)-[~/flaws2logs]
└─$ aws-vault exec security -- aws s3 sync s3://flaws2-logs .
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2305Z_83VTWZ8Z0kiEC7Lq.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2305Z_83VTWZ8Z0kiEC7Lq.json.gz
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_7J9NEIxrjJsrlXSd.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_7J9NEIxrjJsrlXSd.json.gz
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_A1lhv3sWzzRIBFVk.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_A1lhv3sWzzRIBFVk.json.gz
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_rp9i9zxR2Vcpqfnz.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_rp9i9zxR2Vcpqfnz.json.gz
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2235Z_cR9ra7OH1rytWyXY.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2235Z_cR9ra7OH1rytWyXY.json.gz
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2305Z_zKlMhON7EpHala9u.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2305Z_zKlMhON7EpHala9u.json.gz
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_jQajCuiobojD8I4y.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_jQajCuiobojD8I4y.json.gz
download: s3://flaws2-logs/AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_jJW5HfNtz7kOnvcP.json.gz to AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/653711331788_CloudTrail_us-east-1_20181128T2310Z_jJW5HfNtz7kOnvcP.json.gz
Objective 2
Access the Target account
A separate security account that has access to other AWS accounts, as well as access to CloudTrail logs from all other AWS accounts, has been set up for this environment. This is a common best practice that enables Cloud security investigations and incident response.
- Let’s confirm that we have access to this “target” account.
┌──(kali㉿kali)-[~/flaws2logs]
└─$ aws-vault exec security -- aws --profile target_security sts get-caller-identity
{
"UserId": "AROAIKRY5GULQLYOGRMNS:botocore-session-1654835880",
"Account": "653711331788",
"Arn": "arn:aws:sts::653711331788:assumed-role/security/botocore-session-1654835880"
}
# OR
┌──(kali㉿kali)-[~/flaws2logs]
└─$ aws-vault exec target_security -- aws sts get-caller-identity
{
"UserId": "AROAIKRY5GULQLYOGRMNS:1654836198058317660",
"Account": "653711331788",
"Arn": "arn:aws:sts::653711331788:assumed-role/security/1654836198058317660"
}
- Something to observe here is the account ID is now
653711331788
, which is different from the original “security” account which has an account ID of322079859186
- List the buckets for the levels of the Attacker path using the “target_security” account
┌──(kali㉿kali)-[~/flaws2logs]
└─$ aws-vault exec target_security -- aws s3 ls
2018-11-20 14:50:08 flaws2.cloud
2018-11-20 13:45:26 level1.flaws2.cloud
2018-11-20 20:41:16 level2-g9785tw8478k4awxtbox9kk3c5ka8iiz.flaws2.cloud
2018-11-26 14:47:22 level3-oc6ou6dnkw8sszwvdrraxc5t5udrsw3s.flaws2.cloud
2018-11-27 15:37:27 the-end-962b72bjahfm5b4wcktm8t9z4sapemjb.flaws2.cloud
Objective 3
Use jq
for investigation
jq
is a very useful utility that can be used to manipulate JSON data.
Installation
sudo apt-get install jq
Usage
- Navigate into the logs file directory
┌──(kali㉿kali)-[~/flaws2logs]
└─$ cd AWSLogs/653711331788/CloudTrail/us-east-1/2018/11/28/
- Find all files in every subdirectory, recursively, and attempt to gunzip them
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec gunzip {} \;
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2235Z_cR9ra7OH1rytWyXY.json: unknown suffix -- ignored
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2310Z_rp9i9zxR2Vcpqfnz.json: unknown suffix -- ignored
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2310Z_jQajCuiobojD8I4y.json: unknown suffix -- ignored
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2305Z_83VTWZ8Z0kiEC7Lq.json: unknown suffix -- ignored
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2310Z_A1lhv3sWzzRIBFVk.json: unknown suffix -- ignored
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2310Z_7J9NEIxrjJsrlXSd.json: unknown suffix -- ignored
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2310Z_jJW5HfNtz7kOnvcP.json: unknown suffix -- ignored
gzip: ./653711331788_CloudTrail_us-east-1_20181128T2305Z_zKlMhON7EpHala9u.json: unknown suffix -- ignored
- Cat them through jq
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq
Sample JSON log file result
{
"Records": [
{
"eventVersion": "1.05",
"userIdentity": {
"type": "AWSService",
"invokedBy": "ecs-tasks.amazonaws.com"
},
"eventTime": "2018-11-28T22:31:59Z",
"eventSource": "sts.amazonaws.com",
"eventName": "AssumeRole",
"awsRegion": "us-east-1",
"sourceIPAddress": "ecs-tasks.amazonaws.com",
"userAgent": "ecs-tasks.amazonaws.com",
"requestParameters": {
"roleSessionName": "d190d14a-2404-45d6-9113-4eda22d7f2c7",
"roleArn": "arn:aws:iam::653711331788:role/level3"
},
"responseElements": {
"credentials": {
"sessionToken": "FQoGZXIvYXdzEFAaDEbnJXLefTT+kjlmKSKSBNgEUj8tJVL+szjaH5q2npYc2FIPgrLmfkRjK9KqtSW7+lo4WxteBTd77aeAcmIip4GceNBbU86zxGgS1IdNBzEOLnDw6biAzijG0Du/Qazx136qjy+kahHxPlR36C4y/0QrCUZpTFmP3uELsRIKkvhGvuBr6S10pTOZ+GjtUXN3iFV8Ea0KOo/fSP0d4LbZGwI957aJxs2I7N8ji/lKTfwPdq+sxXvSWnaOseinUxZUDS0zdI69CKb6C+qwhR5YTifqyuOvC9OoSlfcBN2FyHpRZf5Bd+Z+mPYTldbAvD/HcdbQo7U4jqlR2WGuXoBfwvypt/Kb6HtPp4g9O0HlTCc7Sb4uiJY81WMbaNFmmYXyj62gi+BN5QaA90YhWn9cU1x9gqt0uEgvSk/RdrwtulTtNyJcuuFvhlD1gaJHGc4eCoWApr+J9nrbPTvSo00sc8IYIVvwOi3NRsmP+ZA9aQOV/qg2L1cYxScQrQ/pKVOnYWJ4XuB0WL8gRYdo1bGI6LWGAtOV+fzVoXU0SfWH7UmPcvttqkWsv1Rr50pspmJneXeY6Ge1szNsvyHqmPFJj7GAXnEKl9xthHK3IaDc6HEprFqYw8SyQqYWGdpeism2S+V4XpIMbQJlC06BxyOg94H0Fiffzs8wwnCUpBU0s69X2tN8sxb4U3FqKl28mwCOuuaweZeGWq5MWxU7Fop2dmjaKN+u/N8F",
"accessKeyId": "ASIAZQNB3KHGNXWXBSJS",
"expiration": "Nov 29, 2018 4:31:59 AM"
}
},
"requestID": "6b7d6c60-f35d-11e8-becc-39e7d43d4afe",
"eventID": "6177ca7e-860e-482c-bde9-50c735af58d6",
"resources": [
{
"ARN": "arn:aws:iam::653711331788:role/level3",
"accountId": "653711331788",
"type": "AWS::IAM::Role"
}
],
"eventType": "AwsApiCall",
"recipientAccountId": "653711331788",
"sharedEventID": "1d18bf74-8392-4496-9dc4-a45cb799b8b4"
},
{
"eventVersion": "1.05",
"userIdentity": {
"type": "AWSService",
"invokedBy": "ecs-tasks.amazonaws.com"
},
"eventTime": "2018-11-28T22:31:59Z",
"eventSource": "sts.amazonaws.com",
"eventName": "AssumeRole",
"awsRegion": "us-east-1",
"sourceIPAddress": "ecs-tasks.amazonaws.com",
"userAgent": "ecs-tasks.amazonaws.com",
"requestParameters": {
"roleSessionName": "d190d14a-2404-45d6-9113-4eda22d7f2c7",
"roleArn": "arn:aws:iam::653711331788:role/ecsTaskExecutionRole"
},
"responseElements": {
"credentials": {
"sessionToken": "FQoGZXIvYXdzEFAaDAhihig4wSQxSSiMjSKSBOW1B0tXzW8SIP7MtRZEikuZamVqb7hEQtmgX5LDeDzpOxpP4G9U8r3cZK74HNErY8W+Scri3Y8NVr/VO7MyzIXnpXiEDo10JfNYnA+urhxB+rYtPF6o8uzm40w/lqdq1/DyOkYYuFRtqWfS4Jy1t83KYAFTuX5IkTGekMidOkIcdnHjsZKQSqKs4U3MQ4doUG4nCyEERDv9yckTPq/R4fKEPTF1BN0jycyiSiR3OTPAWaLGMGxHthAKSA5IXosrPBAq0yD2HhSKZc0kbskCZyGOQCcmVAQK4IdEjyk4Lytc+PEDasvWiGoCQPEqlwwqFJm7EPm838MjCTi4ojN9nVYRP9hFYkXdvnVG6ScwoBfbg135vN1bqYgEKDncW780xUBRYwElM4Q2/6zv7DMj0UegbRJmAwtys4phLrItQqNLWPmBbW/pNgMYoT1IKfzDmuc27AyTHtL8t25hkYOLWZG19EYKm+XeHU9gbs0aDTPssjBFPZp7ssR35IHhgcw5m+etXSoXMxMuHNbVR46ZJ6uieR8roEZ8QfiIijyB8J7i2sH2JOY0HIOonbVYmqdtv++0D+1idTO+6ZbXJIKhEmDmZZDeenF2ZXQGH4PgA4udIdBXnhVLzVFkEKc1MWG3aAhuOhZLvnXkOpkn1XjwfDx3N0UyenOUR4x/gkY9+MEXMZAyO8Va2Y1vNZvnSCvTJtHDKN+u/N8F",
"accessKeyId": "ASIAZQNB3KHGMG7YRUEW",
"expiration": "Nov 29, 2018 4:31:59 AM"
}
},
"requestID": "6b80a0b1-f35d-11e8-becc-39e7d43d4afe",
"eventID": "457af3a9-0b1b-44ca-91e1-8f4a0f873149",
"resources": [
{
"ARN": "arn:aws:iam::653711331788:role/ecsTaskExecutionRole",
"accountId": "653711331788",
"type": "AWS::IAM::Role"
}
],
"eventType": "AwsApiCall",
"recipientAccountId": "653711331788",
"sharedEventID": "5397e1a9-82c7-4a00-9b1c-e44cbd688aa1"
}
]
}
{
- Filter out for only event names
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq '.Records[]|.eventName'
"AssumeRole"
"AssumeRole"
"GetObject"
"GetObject"
"ListBuckets"
"CreateLogStream"
"BatchGetImage"
"GetDownloadUrlForLayer"
"CreateLogStream"
"AssumeRole"
"CreateLogStream"
"CreateLogStream"
"ListImages"
"CreateLogStream"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"Invoke"
"GetObject"
"GetObject"
"GetObject"
"ListObjects"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"GetObject"
"Invoke"
- Filter event name with time stamp
-cr
for printing data in a row|@tsv
for tab separating
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq -cr '.Records[]|[.eventTime, .eventName]|@tsv' | sort 130 ⨯
2018-11-28T22:31:59Z AssumeRole
2018-11-28T22:31:59Z AssumeRole
2018-11-28T23:02:56Z GetObject
2018-11-28T23:02:56Z GetObject
2018-11-28T23:02:56Z GetObject
2018-11-28T23:02:56Z GetObject
2018-11-28T23:02:57Z GetObject
2018-11-28T23:03:08Z GetObject
2018-11-28T23:03:08Z GetObject
2018-11-28T23:03:08Z GetObject
2018-11-28T23:03:08Z GetObject
2018-11-28T23:03:08Z GetObject
2018-11-28T23:03:11Z GetObject
2018-11-28T23:03:11Z GetObject
2018-11-28T23:03:12Z AssumeRole
2018-11-28T23:03:12Z CreateLogStream
2018-11-28T23:03:13Z CreateLogStream
2018-11-28T23:03:13Z Invoke
2018-11-28T23:03:14Z GetObject
2018-11-28T23:03:17Z GetObject
2018-11-28T23:03:18Z GetObject
2018-11-28T23:03:20Z CreateLogStream
2018-11-28T23:03:20Z Invoke
2018-11-28T23:03:35Z CreateLogStream
2018-11-28T23:03:50Z CreateLogStream
2018-11-28T23:04:54Z ListObjects
2018-11-28T23:05:10Z GetObject
2018-11-28T23:05:12Z GetObject
2018-11-28T23:05:12Z GetObject
2018-11-28T23:05:53Z ListImages
2018-11-28T23:06:17Z BatchGetImage
2018-11-28T23:06:33Z GetDownloadUrlForLayer
2018-11-28T23:07:08Z GetObject
2018-11-28T23:07:08Z GetObject
2018-11-28T23:09:28Z ListBuckets
2018-11-28T23:09:36Z GetObject
2018-11-28T23:09:36Z GetObject
- Further querying
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq -cr '.Records[]|[.eventTime, .sourceIPAddress, .userIdentity.arn, .userIdentity.accountId, .userIdentity.type, .eventName]|@tsv' | sort
2018-11-28T22:31:59Z ecs-tasks.amazonaws.com AWSService AssumeRole
2018-11-28T22:31:59Z ecs-tasks.amazonaws.com AWSService AssumeRole
2018-11-28T23:02:56Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:02:56Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:02:56Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:02:56Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:02:57Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:08Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:08Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:08Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:08Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:08Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:11Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:11Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:12Z 34.234.236.212 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole CreateLogStream
2018-11-28T23:03:12Z lambda.amazonaws.com AWSService AssumeRole
2018-11-28T23:03:13Z 34.234.236.212 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole CreateLogStream
2018-11-28T23:03:13Z apigateway.amazonaws.com AWSService Invoke
2018-11-28T23:03:14Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:17Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:18Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:03:20Z 34.234.236.212 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole CreateLogStream
2018-11-28T23:03:20Z apigateway.amazonaws.com AWSService Invoke
2018-11-28T23:03:35Z 34.234.236.212 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole CreateLogStream
2018-11-28T23:03:50Z 34.234.236.212 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole CreateLogStream
2018-11-28T23:04:54Z 104.102.221.250 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole ListObjects
2018-11-28T23:05:10Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:05:12Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:05:12Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:05:53Z 104.102.221.250 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole ListImages
2018-11-28T23:06:17Z 104.102.221.250 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole BatchGetImage
2018-11-28T23:06:33Z 104.102.221.250 arn:aws:sts::653711331788:assumed-role/level1/level1 653711331788 AssumedRole GetDownloadUrlForLayer
2018-11-28T23:07:08Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:07:08Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:09:28Z 104.102.221.250 arn:aws:sts::653711331788:assumed-role/level3/d190d14a-2404-45d6-9113-4eda22d7f2c7 653711331788 AssumedRole ListBuckets
2018-11-28T23:09:36Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
2018-11-28T23:09:36Z 104.102.221.250 ANONYMOUS_PRINCIPAL AWSAccount GetObject
Output data can be copied into Excel or another spreadsheet which can sometimes make the data easier to work with.
Objective 4
Identify credential theft
- Start by first investigating the
ListBuckets
API call
──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq '.Records[]|select(.eventName=="ListBuckets")'
{
"eventVersion": "1.05",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAJQMBDNUMIKLZKMF64:d190d14a-2404-45d6-9113-4eda22d7f2c7",
"arn": "arn:aws:sts::653711331788:assumed-role/level3/d190d14a-2404-45d6-9113-4eda22d7f2c7",
"accountId": "653711331788",
"accessKeyId": "ASIAZQNB3KHGNXWXBSJS",
"sessionContext": {
"attributes": {
"mfaAuthenticated": "false",
"creationDate": "2018-11-28T22:31:59Z"
},
"sessionIssuer": {
"type": "Role",
"principalId": "AROAJQMBDNUMIKLZKMF64",
"arn": "arn:aws:iam::653711331788:role/level3",
"accountId": "653711331788",
"userName": "level3"
}
}
},
"eventTime": "2018-11-28T23:09:28Z",
"eventSource": "s3.amazonaws.com",
"eventName": "ListBuckets",
"awsRegion": "us-east-1",
"sourceIPAddress": "104.102.221.250",
"userAgent": "[aws-cli/1.16.19 Python/2.7.10 Darwin/17.7.0 botocore/1.12.9]",
"requestParameters": null,
"responseElements": null,
"requestID": "4698593B9338B27F",
"eventID": "65e111a0-83ae-4ba8-9673-16291a804873",
"eventType": "AwsApiCall",
"recipientAccountId": "653711331788"
The observed source IP address 104.102.221.250
is not AWS owned and will be viewed as the attacker’s IP.
- Investigate the Role/User that performed the action
level3
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ aws-vault exec target_security -- aws iam get-role --role-name level3
{
"Role": {
"Path": "/",
"RoleName": "level3",
"RoleId": "AROAJQMBDNUMIKLZKMF64",
"Arn": "arn:aws:iam::653711331788:role/level3",
"CreateDate": "2018-11-23T17:55:27+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Description": "Allows ECS tasks to call AWS services on your behalf.",
"MaxSessionDuration": 3600,
"RoleLastUsed": {
"LastUsedDate": "2022-06-07T15:34:21+00:00",
"Region": "us-east-1"
}
}
}
The observed role arn:aws:iam::653711331788:role/level3
is only supposed to be run by the Amazon ECS service ecs-tasks.amazonaws.com
, however the observed source IP from before 104.102.221.250
was confirmed to be a non-AWS IP address.
We can safely assume that this role was compromised since it’s ListBuckets
API call was from a non-AWS IP Address since normal activity would be API calls from the AWS ECS service’ IP Address.
Objective 5
Identify the public resource
- Let’s investigate the
ListImages
,BatchGetImage
, andGetDownloadUrlForLayer
API Calls observed from our previous investigations
ListImages
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq '.Records[]|select(.eventName=="ListImages")'
{
"eventVersion": "1.04",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAIBATWWYQXZTTALNCE:level1",
"arn": "arn:aws:sts::653711331788:assumed-role/level1/level1",
"accountId": "653711331788",
"accessKeyId": "ASIAZQNB3KHGIGYQXVVG",
"sessionContext": {
"attributes": {
"mfaAuthenticated": "false",
"creationDate": "2018-11-28T23:03:12Z"
},
"sessionIssuer": {
"type": "Role",
"principalId": "AROAIBATWWYQXZTTALNCE",
"arn": "arn:aws:iam::653711331788:role/service-role/level1",
"accountId": "653711331788",
"userName": "level1"
}
}
},
"eventTime": "2018-11-28T23:05:53Z",
"eventSource": "ecr.amazonaws.com",
"eventName": "ListImages",
"awsRegion": "us-east-1",
"sourceIPAddress": "104.102.221.250",
"userAgent": "aws-cli/1.16.19 Python/2.7.10 Darwin/17.7.0 botocore/1.12.9",
"requestParameters": {
"repositoryName": "level2",
"registryId": "653711331788"
},
"responseElements": null,
"requestID": "2780d808-f362-11e8-b13e-dbd4ed9d7936",
"eventID": "eb0fa4a0-580f-4270-bd37-7e45dfb217aa",
"resources": [
{
"ARN": "arn:aws:ecr:us-east-1:653711331788:repository/level2",
"accountId": "653711331788"
}
],
"eventType": "AwsApiCall",
"recipientAccountId": "653711331788"
}
BatchGetImage
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq '.Records[]|select(.eventName=="BatchGetImage")'
{
"eventVersion": "1.04",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAIBATWWYQXZTTALNCE:level1",
"arn": "arn:aws:sts::653711331788:assumed-role/level1/level1",
"accountId": "653711331788",
"accessKeyId": "ASIAZQNB3KHGIGYQXVVG",
"sessionContext": {
"attributes": {
"mfaAuthenticated": "false",
"creationDate": "2018-11-28T23:03:12Z"
},
"sessionIssuer": {
"type": "Role",
"principalId": "AROAIBATWWYQXZTTALNCE",
"arn": "arn:aws:iam::653711331788:role/service-role/level1",
"accountId": "653711331788",
"userName": "level1"
}
}
},
"eventTime": "2018-11-28T23:06:17Z",
"eventSource": "ecr.amazonaws.com",
"eventName": "BatchGetImage",
"awsRegion": "us-east-1",
"sourceIPAddress": "104.102.221.250",
"userAgent": "aws-cli/1.16.19 Python/2.7.10 Darwin/17.7.0 botocore/1.12.9",
"requestParameters": {
"imageIds": [
{
"imageTag": "latest"
}
],
"repositoryName": "level2",
"registryId": "653711331788"
},
"responseElements": null,
"requestID": "35ea9256-f362-11e8-86cf-35c48074ab0a",
"eventID": "b2867f3e-810c-47d1-9657-edb886e03fe6",
"resources": [
{
"ARN": "arn:aws:ecr:us-east-1:653711331788:repository/level2",
"accountId": "653711331788"
}
],
"eventType": "AwsApiCall",
"recipientAccountId": "653711331788"
}
GetDownloadUrlForLayer
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ find . -type f -exec cat {} \; | jq '.Records[]|select(.eventName=="GetDownloadUrlForLayer")'
{
"eventVersion": "1.04",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAIBATWWYQXZTTALNCE:level1",
"arn": "arn:aws:sts::653711331788:assumed-role/level1/level1",
"accountId": "653711331788",
"accessKeyId": "ASIAZQNB3KHGIGYQXVVG",
"sessionContext": {
"attributes": {
"mfaAuthenticated": "false",
"creationDate": "2018-11-28T23:03:12Z"
},
"sessionIssuer": {
"type": "Role",
"principalId": "AROAIBATWWYQXZTTALNCE",
"arn": "arn:aws:iam::653711331788:role/service-role/level1",
"accountId": "653711331788",
"userName": "level1"
}
}
},
"eventTime": "2018-11-28T23:06:33Z",
"eventSource": "ecr.amazonaws.com",
"eventName": "GetDownloadUrlForLayer",
"awsRegion": "us-east-1",
"sourceIPAddress": "104.102.221.250",
"userAgent": "aws-cli/1.16.19 Python/2.7.10 Darwin/17.7.0 botocore/1.12.9",
"requestParameters": {
"layerDigest": "sha256:2d73de35b78103fa305bd941424443d520524a050b1e0c78c488646c0f0a0621",
"repositoryName": "level2",
"registryId": "653711331788"
},
"responseElements": null,
"requestID": "3f96ec7f-f362-11e8-bf5d-3380094c69db",
"eventID": "ff4c72f3-4fbd-45d4-9ee3-3834a78f53de",
"resources": [
{
"ARN": "arn:aws:ecr:us-east-1:653711331788:repository/level2",
"accountId": "653711331788"
}
],
"eventType": "AwsApiCall",
"recipientAccountId": "653711331788"
}
- As observed from level2
"requestParameters": {
"imageIds": [
{
"imageTag": "latest"
}
],
"repositoryName": "level2",
"registryId": "653711331788"
- Investigate the policy using the repository name
level2
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ aws-vault exec target_security -- aws ecr get-repository-policy --repository-name level2 1 ⨯
{
"registryId": "653711331788",
"repositoryName": "level2",
"policyText": "{\n \"Version\" : \"2008-10-17\",\n \"Statement\" : [ {\n \"Sid\" : \"AccessControl\",\n \"Effect\" : \"Allow\",\n \"Principal\" : \"*\",\n \"Action\" : [ \"ecr:GetDownloadUrlForLayer\", \"ecr:BatchGetImage\", \"ecr:BatchCheckLayerAvailability\", \"ecr:ListImages\", \"ecr:DescribeImages\" ]\n } ]\n}"
- Clean the results up using jq
┌──(kali㉿kali)-[~/…/us-east-1/2018/11/28]
└─$ aws-vault exec target_security -- aws ecr get-repository-policy --repository-name level2 | jq '.policyText|fromjson'
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AccessControl",
"Effect": "Allow",
"Principal": "*",
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:ListImages",
"ecr:DescribeImages"
]
}
]
}
As observed the Principal
is “*” which means the actions can be performed by anyone including anyone on the internet. Basically, the ECR is public.
Scott mentions that “Ideally, you'd use a tool like CloudMapper to scan an account for public resources like this before you trace back an attack.”
Objective 6
Use Athena
Log in to your personal AWS account and access Athena
- Create a database
- Switch to the flaws2 database you just created
- Run the following
CREATE EXTERNAL TABLE `cloudtrail`(
`eventversion` string COMMENT 'from deserializer',
`useridentity` struct<type:string,principalid:string,arn:string,accountid:string,invokedby:string,accesskeyid:string,username:string,sessioncontext:struct<attributes:struct<mfaauthenticated:string,creationdate:string>,sessionissuer:struct<type:string,principalid:string,arn:string,accountid:string,username:string>>> COMMENT 'from deserializer',
`eventtime` string COMMENT 'from deserializer',
`eventsource` string COMMENT 'from deserializer',
`eventname` string COMMENT 'from deserializer',
`awsregion` string COMMENT 'from deserializer',
`sourceipaddress` string COMMENT 'from deserializer',
`useragent` string COMMENT 'from deserializer',
`errorcode` string COMMENT 'from deserializer',
`errormessage` string COMMENT 'from deserializer',
`requestparameters` string COMMENT 'from deserializer',
`responseelements` string COMMENT 'from deserializer',
`additionaleventdata` string COMMENT 'from deserializer',
`requestid` string COMMENT 'from deserializer',
`eventid` string COMMENT 'from deserializer',
`resources` array<struct<arn:string,accountid:string,type:string>> COMMENT 'from deserializer',
`eventtype` string COMMENT 'from deserializer',
`apiversion` string COMMENT 'from deserializer',
`readonly` string COMMENT 'from deserializer',
`recipientaccountid` string COMMENT 'from deserializer',
`serviceeventdetails` string COMMENT 'from deserializer',
`sharedeventid` string COMMENT 'from deserializer',
`vpcendpointid` string COMMENT 'from deserializer')
ROW FORMAT SERDE
'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT
'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
's3://flaws2-logs/AWSLogs/653711331788/CloudTrail';
- Then run the following
select eventtime, eventname from cloudtrail;
Regular SQL queries can then be run against this data, e.g
SELECT eventname, eventtime,
count(eventname) AS mycount
FROM cloudtrail
GROUP BY eventname, eventtime
ORDER BY mycount;
Closing notes from Scott:
“Athena is great for incident response because you don't have to wait for the data to load anywhere, just define the table in Athena and start querying it. If you do so, you should also create partitions which will reduce your costs by helping you query only against a specific day. Alex Smolen gives a good explanation on how to do that in his article Partitioning CloudTrail Logs in Athena.”
The End
This was a fun series of challenges and a great learning experience. Some key skills learned:
- Assuming roles in other AWS accounts
- Reading IAM and resource policies
- Querying JSON logs with JQ & Athena
- Using SQL
- Working with CloudTrail logs