~/blog/information-leak-in-error-pagesPOSTED
von Shady Nathan Tawfik · 20. Juni 2026 · 4 min

Fehlerseiten sind ein unbeabsichtigter Endpunkt für API-Dokumentation

In einer gemischten Umgebung tauchten dieselben drei Lecks immer wieder auf: Stack Traces, Framework-Banner und absolute Dateisystempfade. Jedes davon kartiert die Internas kostenlos.

Interaktiv[blog-2.0]

TL;DR: Fehlerseiten sind ein unbeabsichtigter API-Dokumentations-Endpunkt. In einer gemischten Landschaft fand ich Stack Traces, Framework-Banner und absolute Dateisystempfade, und ich zeige die Drei-Zeilen-Fixes an der Edge.

Ausgangslage

Ich habe das Ziel als Black-Box geprüft: keine Zugangsdaten, nur die öffentlich sichtbare Fläche. Das ist, was ich sah, bevor ich etwas angefasst habe.

Eine gemischte Umgebung aus mehreren Webplattform-Generationen, in einem Durchgang geprüft, darunter ein Legacy-CMS, eine moderne Framework-Anwendung und eine statische Marketingseite. Im Fokus stand, was die Plattform einem unauthentifizierten Besucher bei Fehlern verrat.

Ich habe alle identifizierenden Informationen entfernt. Das Ziel wird ausschließlich nach Branche und Technologieklasse beschrieben, damit das Muster übertragbar bleibt.

Das Muster

Das Muster, das ich erkannt habe — und das ich in ähnlichen Landschaften immer wieder finde:

Information Disclosure wird meist als Liste von Zeichenketten berichtet, die in Antworten gefunden wurden: eine Versionsnummer, ein Dateipfad, ein Framework-Name. So geführt wirkt es wie Hausarbeit. Die Zeichenketten sind das Symptom. Der Mechanismus ist allgemein: die Plattform unterscheidet nicht zwischen dem Verursacher des Fehlers und dem Leser der Antwort.

Drei Formen tauchen wiederkehrend auf. Entwicklungsmodus aktiv, wo das Framework einen vollständigen Trace mit Quellkontext rendert. Generische Fehlerseiten, die im Footer weiterhin Produkt und Version nennen. Und Catch-all-Routen, die jeden unbekannten Pfad mit Status zwei beantworten.

Nichts davon gewährt Zugriff. Zusammen komprimieren sie eine mehrstündige Aufklärung auf einen Nachmittag, weil der Angreifer Framework-Versionen, Plugin-Verzeichnisse und Deployment-Layout nicht mehr erraten muss.

TL;DR: In einer gemischten Umgebung tauchten dieselben drei Lecks immer wieder auf: Stack Traces, Framework-Banner und absolute Dateisystempfade. Jedes davon kartiert die Internas kostenlos.

Befunde

SchweregradBefundNachweis
HOCHVollständiger Stack Trace für anonyme BesucherQuellkontext und lokale Werte enthalten
MITTELFramework-Banner und Version in generischen FehlerseitenPlattform und Release identifizierbar
MITTELAbsolute Dateisystempfade preisgegebenDeployment-Layout und Benutzernamen
MITTELCatch-all-Routing beantwortet jeden Pfad mit ErfolgKein Not-Found-Status in der Anwendung
NIEDRIGDebug-Werkzeug-Endpunkte erreichbarDiagnose-Seiten ohne Zugangsschutz
NIEDRIGInterne Hostnamen in Response-HeadernNamenskonvention des Backends sichtbar

Der Angriffspfad

Malformed requestFramework error handlerDevelopment mode still onTrace with source contextFramework and version identifiedKnown CVE selectedTargeted exploit rather than blind scan

Reproduktion im Labor

Jeder Befehl unten zielt auf einen Lab-Container, den ich selbst kontrolliere. Nichts davon ist auf ein laufendes System gerichtet.

Fehler in eigener Lab-Umgebung provozieren und die Antwort lesen

~/code/bash bash
# Ziel: Ihre eigene Lab-Umgebung auf localhost
# 1. fehlerhafte Requests, die typischerweise den Debug-Pfad auslösen
$ for p in '/%' '/?q[]=' '/index.php?file=../../../../etc/passwd' '/nonexistent/deep/path'; do
    printf '%s -> %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:8080$p")"
  done

# 2. den Body der fehlgeschlagenen Anfrage lesen
$ curl -s 'http://localhost:8080/?q[]=' | grep -iE 'traceback|stack trace|\.py"|\.php"|File "|/var/www|/home/' | head

# 3. Header, die die Plattform beschreiben
$ curl -sI http://localhost:8080/ | grep -iE 'x-powered-by|x-generator|server|x-aspnet|via:'

Erkennung in eigener Infrastruktur

Disclosure-Sweep über eine eigene Hostliste

~/code/bash bash
# schreibgeschützter Sweep, eine Zeile je Befund
$ for h in example.org example.com; do
  printf '%s: ' "$h"
  curl -sI "https://$h/" | grep -icE 'x-powered-by|x-generator|x-aspnet-version' | tr -d '\n'
  printf ' banner-treffer, '
  curl -s "https://$h/does-not-exist-$(date +%s)" | grep -icE 'traceback|stack trace|/var/www|/home/[a-z]+/' | tr -d '\n'
  printf ' trace-treffer\n'
done

Catch-all-Routing erkennen, das fehlende Seiten versteckt

~/code/bash bash
# jeder Pfad muss 404 liefern, nicht 200
$ for p in /nope /nope/deeper /admin /wp-content/nope; do
  printf '%s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' https://example.org$p)"
done

Behebung

Debug-Ausgabe abschalten und den Fehler-Body anpassen

~/code/python python
# Django: die Debug-Seite niemals in Produktion ausliefern
DEBUG = False
ALLOWED_HOSTS = ["example.org"]

# Flask: echten Error-Handler statt des interaktiven Debuggers
@app.errorhandler(500)
def server_error(err):
    app.logger.exception("unhandled error")
    return "Internal error", 500   # kein Trace, keine Pfade

Statische Fehlerseite an der Edge ausliefern, ohne Upstream-Detail

~/code/nginx nginx
server {
    error_page 500 502 503 504 /errors.html;
    location = /errors.html { internal; root /var/www/html; }
}

# und die Plattform nicht mehr verraten
server_tokens off;
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;

Sobald ein Angreifer Zugriff hat, kann er folgende Aktionen durchführen:

Vollständiger Stack Trace für anonyme Besucher
Framework-Banner und Version in generischen Fehlerseiten
Absolute Dateisystempfade preisgegeben
Catch-all-Routing beantwortet jeden Pfad mit Erfolg

Fazit — was ich daraus mitnehme

  • Versionsstrings sind Aufklärung, die Sie kostenlos übergeben.
  • Entwicklungsmodus in Produktion ist ein Konfigurationsfehler mit grosser Reichweite.
  • Catch-all-200-Antworten zerstören auch Ihr eigenes Monitoring.
  • Beheben Sie die Freigabe an der Edge, wo Sie sie kontrollieren, nicht in jeder Anwendung.

Nur zu Bildungszwecken. Alle reproduzierbaren Befehle zielen auf meine eigene Lab-Umgebung (localhost), nie auf ein laufendes System. © 2026