MrRSS: Stored XSS über noscript im Artikelinhalt
Wer HTML in einen Feed setzt, führt über ein noscript-Element JavaScript in der Herkunft von MrRSS aus, sobald das Opfer den Artikel öffnet.
Advisory-ID: TP-2026-125
Produkt: MrRSS (plattformübergreifender RSS-Reader mit Desktop- und Servermodus)
Schwachstellentyp: Stored Cross-Site Scripting (CWE-79)
CVE: nicht beantragt
CVSS 3.1: 8.1 (Hoch) · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
Betroffene Versionen: <= 1.3.40
Hersteller-Advisory: GHSA-8q87-xm7v-xv7q
Behoben in: 1.3.41
Gemeldet: 22. Oktober 2026
Zusammenfassung
MrRSS ist ein RSS-Reader, der den HTML-Inhalt von Feed-Artikeln erst serverseitig durch einen Allowlist-Filter und danach clientseitig durch eine Hilfsfunktion für Lazy Loading schickt. Der Filter entfernt aktive Elemente per Namensliste, doch noscript fehlt in dieser Liste. Der Go-Parser behandelt den Inhalt von noscript als rohen Text, den die Attributprüfung nie ansieht, und der Browser parst denselben Text im template-Rundlauf wieder als Markup. Wer HTML in einen Feed-Eintrag setzt, führt so JavaScript in der Herkunft der Anwendung aus, sobald das Opfer den Artikel öffnet, auf einer unveränderten Installation mit einem Klick und ohne Konto.
Ursache
PrepareArticleContent entfernt aktive Elemente über eine Namensliste, in der noscript fehlt (internal/utils/textutil/article_html.go:50). Der dort verwendete Go-Parser läuft mit aktiviertem Scripting, sodass die Kinder eines noscript-Elements einen einzigen Textknoten bilden, den die Attribut-Allowlist nie prüft und der Serializer unverändert zurückschreibt (internal/utils/textutil/article_html.go:64-116). withLazyImages weist diesen String template.innerHTML zu und gibt template.innerHTML zurück, mit dem Kommentar, der Inhalt sei vom Backend bereits bereinigt (frontend/src/utils/lazyImages.ts:11-30). Der Inhalt eines template gehört zu einem Dokument ohne Browsing-Kontext, dort läuft das Parsen mit abgeschaltetem Scripting, und die Kinder von noscript werden zu echten Elementen (frontend/src/utils/lazyImages.ts:11-30). ArticleContent.vue:179-181 reicht das Ergebnis an ArticleBody.vue:55 weiter, das es per v-html rendert, während der Test internal/utils/textutil/article_html_test.go:23-32 nur die Filterausgabe auf onerror prüft.
Proof of Concept
<item><title>payload</title>
<description><p>marker</p><p><noscript></p><img src=x onerror="fetch('/api/settings').then(r=>r.json()).then(j=>document.documentElement.setAttribute('data-ghsa','keys='+Object.keys(j).length))"></noscript></p></description></item>
GET /api/articles/content?id=1
-> "<p>marker</p><p><noscript></p><img src=x onerror=\"...\"></noscript></p>"
gerenderter DOM nach einem Klick:
-> <p>marker</p><p><noscript></noscript></p><img src="x" onerror="..." loading="lazy" decoding="async">
Das schließende </p> vor dem <img> beendet das vor noscript geöffnete Element, sodass der Template-Parser das Bild außerhalb von noscript erzeugt. Die Attribute loading und decoding stammen von withLazyImages und zeigen, dass der ausführende Knoten aus dem Rundlauf kommt; ein nacktes <img onerror> als Gegenprobe entfernt der Filter.
Auswirkung
- Ausführung von JavaScript in der Herkunft der Anwendung, ausgelöst durch HTML in einem beliebigen Feed, den das Opfer liest.
- Lese- und Schreibzugriff auf die unauthentifizierte lokale API, im Test alle 147 Einstellungsschlüssel, dazu gespeicherte Zugangsdaten von Integrationen, OPML-Export und Artikelbestand.
- Im Servermodus gibt es keine Anmeldung, das Skript handelt mit den Rechten des Instanzbesitzers.
- Unveränderte Installation, Standardansicht, ein Klick, kein Konto.
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.
