It was way past midnight, maybe around 03:12 AM. While I was monitoring the traffic logs of a new microservice within testCompany, something caught my eye. A PDF generation service was rendering content by taking a URL parameter from the user. But in the logs, I noticed the service was taking a 'meaningless' journey toward 169[.]254[.]169[.]254—the metadata service of the cloud provider—within its own internal network. I remember my sleepiness vanishing instantly. This wasn't just a simple bug; it was a Server-Side Request Forgery (SSRF) vulnerability that could leak all the authorized keys (IAM credentials) to the outside world.
As a Red Team Lead, this is one of the most common attacks I encounter in the field. Developers often underestimate it, saying 'What's the big deal? It just visits a link,' but in reality, it's a Trojan horse that tears down the fortress walls from the inside. Today, we’re going deep into this topic—down into those dark corridors within the code.
How an Innocent Function Becomes a Weapon
As developers, we love making things easier. Fetching a user's profile picture from a URL, taking a screenshot of a webpage, or converting an invoice into a PDF are great features. However, while implementing these, we often forget that the server turns into a 'request machine.'
Let’s look at a vulnerable Node.js code snippet (defanged, of course):
// ZAFİYETLİ KOD ÖRNEĞİ
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/generate-pdf', async (req, res) => {
const targetUrl = req.query.url; // Kullanıcıdan kontrolsüz gelen URL
try {
// Sunucu, kullanıcının verdiği URL'e sorgu atıyor
const response = await axios.get(targetUrl);
// ... PDF oluşturma işlemleri ...
res.send("İşlem başarılı");
} catch (error) {
res.status(500).send("Hata oluştu");
}
});
The problem here is this: instead of typing http://google.com into the url parameter, an attacker types internal services that the server can reach but are closed to the internet. For example: http://127.0.0.1:8080/admin/delete-user?id=1. The server says 'hello' to its own internal admin panel and executes the command in seconds.
Crashing the Metadata Services
The game changes significantly in Cloud environments. On platforms like AWS, Google Cloud, or Azure, every instance accesses its own metadata via 169[.]254[.]169[.]254 (link-local address). When an attacker finds an SSRF, the first payload they'll try is:
http://169[.]254[.]169[.]254/latest/meta-data/iam/security-credentials/[ROLE_NAME]
If this request succeeds, temporary AccessKeyId, SecretAccessKey, and Token information representing all the server's permissions falls right into the attacker's terminal. Now, the attacker can act as if they are your server. What I caught in the testCompany scenario was exactly this attempt. Fortunately, we managed to restrict those permissions with a quick intervention.
Filtering? Don't Make Me Laugh
Many people think they can prevent this by...
