This is a simple write up for the first challenge I attempted on CyberDefenders, which can be found here.

Instructions

Use the provided credentials to access AWS cloud trail logs and answer the questions.

Scenario

Welcome, Defender! As an incident responder, we’re granting you access to the AWS account called “Security” as an IAM user. This account contains a copy of the logs during the time period of the incident and has the ability to assume the “Security” role in the target account so you can look around to spot the misconfiguration that allowed for this attack to happen.

Environment

The credentials above give you access to the Security account, which can assume the role of “security” in the Target account. You also have acces to an S3 bucket, named flaws2_logs, in the Security account, that contains the CloudTrail logs recorded during a successful compromise.

Question 1

What is the full AWS CLI command used to configure credentials?

This question can be answered by reading the docs. The answer is aws configure.

Question 2

What is the ‘creation’ date of the bucket ‘flaws2-logs’?

This question can be answered by logging into the AWS portal with the credentials provided in the challenge, navigating to the S3 Buckets dashboard, and observing the metadata provided for the flaws2-logs bucket. Note that the creation time displayed is, at least for me, in Eastern Time (UTC -5:00), so some simple arithmetic is needed to get it into the UTC time zone requested by the answer. The answer is 2018-11-19 20:54:31 UTC.

Environment Setup

The following questions will be answered by analyzing the data in Python. So let’s load some packages, read in the data, and convert all of the logs to a single Pandas dataframe.

import pandas as pd
import os
import json

files = os.listdir(".")
files = [file for file in files if file.endswith(".json")]

data = []
for file in files:
    with open(file, "r") as f:
        data.append(json.load(f))
        
for datum in data[1:]:
    data[0]["Records"].extend(datum["Records"])

df = pd.DataFrame(data=data[0]["Records"])
df.head()
eventVersion userIdentity eventTime eventSource eventName awsRegion sourceIPAddress userAgent requestParameters responseElements ... requestID eventID readOnly resources eventType recipientAccountId sharedEventID errorCode errorMessage managementEvent
0 1.05 {'type': 'AWSAccount', 'principalId': '', 'acc... 2018-11-28T23:09:36Z s3.amazonaws.com GetObject us-east-1 104.102.221.250 [Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_... {'bucketName': 'the-end-962b72bjahfm5b4wcktm8t... None ... EDFBFC9CE11E755F ea33682d-0829-40c1-9820-bd721b9aede8 True [{'type': 'AWS::S3::Object', 'ARN': 'arn:aws:s... AwsApiCall 653711331788 a59b4ac8-6a51-44ff-ab76-e66f75bd95ce NaN NaN NaN
1 1.05 {'type': 'AWSAccount', 'principalId': '', 'acc... 2018-11-28T23:09:36Z s3.amazonaws.com GetObject us-east-1 104.102.221.250 [Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_... {'bucketName': 'the-end-962b72bjahfm5b4wcktm8t... None ... 9880010F3D39F3AC dee6f6a3-f18a-40db-a6fd-b96d05502266 True [{'type': 'AWS::S3::Object', 'ARN': 'arn:aws:s... AwsApiCall 653711331788 f8c6cdc8-6ec1-4e14-9a0e-f300b16e282e NaN NaN NaN
2 1.05 {'type': 'AWSService', 'invokedBy': 'ecs-tasks... 2018-11-28T22:31:59Z sts.amazonaws.com AssumeRole us-east-1 ecs-tasks.amazonaws.com ecs-tasks.amazonaws.com {'roleSessionName': 'd190d14a-2404-45d6-9113-4... {'credentials': {'sessionToken': 'FQoGZXIvYXdz... ... 6b7d6c60-f35d-11e8-becc-39e7d43d4afe 6177ca7e-860e-482c-bde9-50c735af58d6 NaN [{'ARN': 'arn:aws:iam::653711331788:role/level... AwsApiCall 653711331788 1d18bf74-8392-4496-9dc4-a45cb799b8b4 NaN NaN NaN
3 1.05 {'type': 'AWSService', 'invokedBy': 'ecs-tasks... 2018-11-28T22:31:59Z sts.amazonaws.com AssumeRole us-east-1 ecs-tasks.amazonaws.com ecs-tasks.amazonaws.com {'roleSessionName': 'd190d14a-2404-45d6-9113-4... {'credentials': {'sessionToken': 'FQoGZXIvYXdz... ... 6b80a0b1-f35d-11e8-becc-39e7d43d4afe 457af3a9-0b1b-44ca-91e1-8f4a0f873149 NaN [{'ARN': 'arn:aws:iam::653711331788:role/ecsTa... AwsApiCall 653711331788 5397e1a9-82c7-4a00-9b1c-e44cbd688aa1 NaN NaN NaN
4 1.04 {'type': 'AssumedRole', 'principalId': 'AROAIB... 2018-11-28T23:06:17Z ecr.amazonaws.com BatchGetImage us-east-1 104.102.221.250 aws-cli/1.16.19 Python/2.7.10 Darwin/17.7.0 bo... {'imageIds': [{'imageTag': 'latest'}], 'reposi... None ... 35ea9256-f362-11e8-86cf-35c48074ab0a b2867f3e-810c-47d1-9657-edb886e03fe6 NaN [{'ARN': 'arn:aws:ecr:us-east-1:653711331788:r... AwsApiCall 653711331788 NaN NaN NaN NaN

5 rows × 21 columns


Question 3

What is the name of the first generated event according to time?

df.sort_values("eventTime", ascending=True, ignore_index=True).loc[0, "eventName"]

This gives us a value of AssumeRole, which is our answer.

Question 4

What source IP address generated the event dated 2018-11-28 at 23:03:20 UTC?

df.query("eventTime == '2018-11-28T23:03:20Z'")
eventVersion userIdentity eventTime eventSource eventName awsRegion sourceIPAddress userAgent requestParameters responseElements ... requestID eventID readOnly resources eventType recipientAccountId sharedEventID errorCode errorMessage managementEvent
9 1.04 {'type': 'AssumedRole', 'principalId': 'AROAIB... 2018-11-28T23:03:20Z logs.amazonaws.com CreateLogStream us-east-1 34.234.236.212 awslambda-worker None None ... cc9ae337-f361-11e8-894e-cbc2b0778d92 483557d2-2b35-4fc6-b682-ff5dbc96eccf NaN NaN AwsApiCall 653711331788 NaN AccessDenied User: arn:aws:sts::653711331788:assumed-role/l... NaN
36 1.06 {'type': 'AWSService', 'invokedBy': 'apigatewa... 2018-11-28T23:03:20Z lambda.amazonaws.com Invoke us-east-1 apigateway.amazonaws.com apigateway.amazonaws.com {'functionName': 'arn:aws:lambda:us-east-1:653... None ... cc96765b-f361-11e8-a2d8-2b201bd316c5 949e83c0-0d98-4b7d-8845-5e2fe3eafde4 False [{'accountId': '653711331788', 'type': 'AWS::L... AwsApiCall 653711331788 a63b106b-e331-4778-a6c0-64e397216fde NaN NaN False

2 rows × 21 columns

Two results are returned, but the answer is obviously 34.234.236.212.

Question 5

Which IP address does not belong to Amazon AWS infrastructure?

(
    df
    .groupby(["sourceIPAddress", "userAgent"])
    .size()
)
sourceIPAddress           userAgent                                                                                                                  
104.102.221.250           [Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36]    22
                          [aws-cli/1.16.19 Python/2.7.10 Darwin/17.7.0 botocore/1.12.9]                                                                   2
                          aws-cli/1.16.19 Python/2.7.10 Darwin/17.7.0 botocore/1.12.9                                                                     3
34.234.236.212            awslambda-worker                                                                                                                5
apigateway.amazonaws.com  apigateway.amazonaws.com                                                                                                        2
ecs-tasks.amazonaws.com   ecs-tasks.amazonaws.com                                                                                                         2
lambda.amazonaws.com      lambda.amazonaws.com                                                                                                            1
dtype: int64

There is only one IP address whose User Agent string does not mention AWS, which is 104.102.221.250.

Question 6

Which user issued the ‘ListBuckets’ request?

df.query("eventName == 'ListBuckets'")['userIdentity'].values
array([{'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'}}}],
      dtype=object)

The last key in the JSON data is userName, which has a value of level3, which is our answer.

Question 7

What was the first request issued by the user ‘level1’?

(
    df
    .loc[df["userIdentity"].astype(str).str.contains("level1")]
    .sort_values("eventTime", ascending=True, ignore_index=True)
    .loc[0, "eventName"]
)

The answer is CreateLogStream.