ApostropheCMS @apostrophecms/file Pretty-URL Unauthenticated SSRF via Host Header (CVE-2026-53607) -DC-Aug2026-1203

Listen to this Post

How CVE-2026-53607 Works

ApostropheCMS is an open-source Node.js content management system. In versions up to and including 4.30.0, when `prettyUrls: true` is enabled on the `@apostrophecms/file` module—a documented SEO feature for serving uploaded files at clean URLs—the public pretty-URL handler builds the upstream URL using the raw `Host` HTTP request header. The code constructs the proxy URL as: proxyUrl =${req.protocol}://${req.get(‘host’)}${uglyUrl}“.
This URL is then fetched and the response body plus headers are streamed directly back to the requester. Because the `Host` header is fully attacker-controlled, an unauthenticated remote attacker can pivot the Apostrophe process to issue outbound HTTP requests against any host it can reach on the private network.
The vulnerable code resides in `modules/@apostrophecms/file/index.js` where the public GET route is registered when prettyUrls: true. After validating the slug from the URL, the system retrieves the file from the database and generates an uglyUrl. If this URL starts with a slash, it is prefixed with `req.protocol` and req.get('host')—the unsanitized Host header from the incoming request. The `stream-proxy.js` module then executes `fetch(url)` on this attacker-controlled URL and pipes the response back to the client.
Express does not validate or restrict the Host header, and Apostrophe does not check the constructed `proxyUrl` against an allowlist. The upstream’s body and content-type are forwarded verbatim, meaning any response the targeted host returns at the constrained path will reach the attacker.
The path component is constrained to `/uploads/attachments/.` (built from a local-DB lookup). This constraint plus cuid uniqueness neutralizes cross-instance data exfiltration and rules out cloud metadata endpoints like AWS IMDS (/latest/meta-data/...), GCP (/computeMetadata/v1/...), and Azure (/metadata/...).
What remains are blind-SSRF residuals: network-topology mapping via response-code or timing differences, and banner/version disclosure from verbose reverse-proxy or WAF 404 bodies. The attack requires only the public pretty-URL endpoint and one publicly-known file slug, both trivially available in normal CMS operation. No fixed release exists as of publication.

DailyCVE Form:

Platform: ApostropheCMS
Version: <=4.30.0
Vulnerability: Unauthenticated SSRF
Severity: Low (3.7)
date: 2026-06-12

Prediction: Patch 2026-08-15

What Undercode Say

Analytics:

The vulnerability stems from improper handling of HTTP request headers during the pretty-URL resolution process. The Host header is used to construct an internal fetch URL without validation, creating a server-side proxy that can be redirected to internal services. This behavior aligns with CWE-918 (Server-Side Request Forgery).
The CVSS v3.1 score is 3.7 (Low) with vector: AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N. AC:H is assigned because meaningful information disclosure requires a specific target shape (verbose WAF/proxy 404, banner disclosure, or exploitable response-code/timing side channels). C:L captures the residual information-disclosure surface.

Bash Commands & Code:

Vulnerable code snippet from `modules/@apostrophecms/file/index.js`:

</dt>
<dt>const proxyUrl = uglyUrl.startsWith('/')</dt>
<dt>? `${req.protocol}://${req.get('host')}${uglyUrl}` // <-- sink</dt>
<dd>uglyUrl;
return await streamProxy(req, proxyUrl, { error: self.apos.util.error });

`lib/stream-proxy.js` fetch call:

let response;
try { response = await fetch(url); } // <-- attacker-steered fetch
catch (e) { return send502(e); }

Detection command to check if `prettyUrls` is enabled:

grep -r "prettyUrls: true" ./modules/@apostrophecms/file/index.js

Test for SSRF vulnerability:

curl -sS -H 'Host: internal-service' "http://target.com/files/known-slug.pdf"

Exploit

Prerequisites:

– `prettyUrls: true` enabled on `@apostrophecms/file`
– `uploadfs` is local (default; S3/CDN deployments are not affected)
– At least one file uploaded with a known slug (publicly enumerable)

Attack Scenario (Docker PoC):

Three services on an isolated network: MongoDB, an internal service (returning a fake secret), and Apostrophe on port 3000 (the only port reachable from the host).

Exploit script (`exploit.sh`):

Confirm internal target is not reachable from the host
curl --max-time 2 -s http://internal/ || echo "(unreachable)"
Attack: pretty URL with attacker-supplied Host header
curl -sS -H 'Host: internal' "http://127.0.0.1:3000/files/poc.pdf"

Observed Output:

  • Normal request (Host: apos): HTTP 502, upstream media error
  • Attacker request (Host: internal): HTTP 200, returns internal service response body
    The internal service is unreachable from the host, but Apostrophe fetches it on the attacker’s behalf and pipes the response body straight back.

Protection

Immediate Mitigations (Network Level):

  • Implement firewall rules restricting outbound HTTP traffic from application servers, particularly to internal network segments
  • Deploy network segmentation to limit the potential impact of successful exploitation
  • Enhance security monitoring to detect unusual outbound HTTP requests originating from CMS servers

Code-Level Fixes:

  1. In modules/@apostrophecms/file/index.js: Use a server-trusted absolute base URL instead of req.get('host'):
    const proxyUrl = uglyUrl.startsWith('/')
    ? `${self.apos.baseUrl || req.baseUrl}${uglyUrl}`
    : uglyUrl;
    
  2. In lib/stream-proxy.js: Enforce a strict origin allowlist (configured Apostrophe base URL + any configured CDN host) before calling fetch.
  3. Regression Test: Set `Host: 169.254.169.254` (or any non-configured host) on `/files/.` and assert the upstream fetch is not issued / the response is a 4xx.

Configuration Workaround:

  • Disable `prettyUrls` for file modules when possible
  • Validate and sanitize all user-controlled input, especially HTTP headers

Impact

Authenticated Status: Unauthenticated remote attacker

Attack Vector: Network (AV:N)

Confidentiality Impact: Low (C:L) – residual information disclosure

What Is NOT Exploitable:

  • Cloud metadata endpoints (AWS IMDS, GCP, Azure) – paths don’t overlap with `/uploads/attachments/`
    – Cross-instance data exfiltration – neutralized by cuid uniqueness
  • Redis admin, Elasticsearch, and most internal API surfaces

What Remains Exploitable:

  • Network-topology mapping via response-code or timing differences across internal hosts
  • Banner/version disclosure from verbose reverse-proxy or WAF 404 bodies
  • Bypassing network egress controls – outbound requests originate from the Apostrophe server rather than the attacker

CVSS Score: 3.7 (Low)

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top