const isLocalhost = (url) => const host = new URL(url).hostname; return host === 'localhost' ; if (isLocalhost(url)) return res.status(400).send('Localhost requests blocked');
GET /api/Image?url=https://example.com/image.png HTTP/1.1 The server code (simplified) looks like: owasp juice shop ssrf
Abstract Server-Side Request Forgery (SSRF) remains a critical web security vulnerability, often enabling internal network reconnaissance, port scanning, and cloud metadata theft. OWASP Juice Shop, a modern intentionally vulnerable web app, contains multiple SSRF challenges that simulate real-world misconfigurations. This paper dissects the Juice Shop SSRF attack surface, demonstrates exploitation techniques, and discusses detection and prevention strategies. 1. Introduction OWASP Juice Shop is a Node.js/Express-based application packed with vulnerabilities from the OWASP Top 10. Among its medium-difficulty challenges is SSRF (Server-Side Request Forgery) — specifically the challenge titled “SSRF” (ID: ssrf ) and related endpoints that allow an attacker to make the server perform arbitrary HTTP requests. const isLocalhost = (url) => const host = new URL(url)
curl "http://localhost:3000/api/Image?url=http://localhost:3000/encryptionkey.txt" HTTP 200 with the encryption key in the body (may be text/plain despite image content-type header). 5. Impact Assessment | Attack Vector | Impact | |---------------|--------| | Localhost file read | Exposure of source code, config files, secrets | | Internal port scan | Discovery of admin panels, databases, Redis, Jenkins | | Cloud metadata theft | IAM credentials, access tokens → cloud account compromise | | Service interaction (e.g., Redis, Memcached) | Potential RCE via protocol smuggling | curl "http://localhost:3000/api/Image
In Juice Shop, the impact is deliberately limited to reading a single file, but in real apps, SSRF often leads to complete internal network compromise. 6.1 Allowlist-Based Input Validation const ALLOWED_HOSTS = ['images.trusted.com', 'cdn.example.com']; const urlObj = new URL(userUrl); if (!ALLOWED_HOSTS.includes(urlObj.hostname)) return res.status(403).send('Host not allowed');
http://[::1]:3000/encryptionkey.txt
GET /api/Image?url=http://localhost:3000/encryptionkey.txt If the challenge is active, the server will fetch that internal resource and return its content inside the image response (or as plain text if content type mismatches).