Exploiting AWS 3 - Defender's Perspective (Flaws2.Cloud)

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.

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 of 322079859186
  • 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, and GetDownloadUrlForLayer 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

image.png Untitled

  • Switch to the flaws2 database you just created

image.png

  • 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;

image.png Untitled

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.

Alex Smolen’s Article

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

Did you find this article valuable?

Support Day Cyberwox by becoming a sponsor. Any amount is appreciated!