
CVE-2026-39154, gespeichertes XSS im CometChat JS SDK
Dieses Verzeichnis enthält eine ehrliche, eigenständige Reproduktion der Render-Senke
hinter CVE-2026-39154: ein gespeicherter Cross-Site-Scripting-Fehler im CometChat
JavaScript SDK 3.0.12, bei dem das Feld data.text einer Gruppennachricht in
die Clients anderer Mitglieder gerendert wird, ohne kontextbezogene Ausgabekodierung.
Es läuft vollständig lokal mit synthetischen Daten. Es kontaktiert, prüft oder authentifiziert sich nicht beim Live-CometChat-Dienst oder bei Dritten. Es ist eine lokale Demonstration ausschließlich des verwundbaren Client-Renderpfads.
Passendes PoC-Repository (im Writeup genannt): https://github.com/defineid/Wildfire
Writeup: writeup-cometchat-xss.html in diesem Portfolio.
| Feld | Wert |
|---|---|
| CVE | CVE-2026-39154 |
| Hersteller / Produkt | CometChat · JavaScript SDK 3.0.12 |
| Klasse | Gespeichertes Cross-Site-Scripting (CWE-79) |
| Endpunkt | POST /v3.0/messages · Injektionsfeld data.text |
| Empfänger | receiverType: group |
| Interaktion | Keine; die gespeicherte Payload wird automatisch ausgeführt, wenn ein Mitglied die Gruppe öffnet |
| Offenlegung | Im Februar 2026 an CometChat gemeldet (Anfrage 42660); Hersteller hat als Solved markiert; CVE von MITRE zugewiesen |
| Referenz | https://www.cve.org/CVERecord?id=CVE-2026-39154 |
Hinweis zur korrigierten Version: Das veröffentlichte Writeup gibt an, dass der Hersteller das Problem behoben hat („VENDOR: SOLVED“), nennt aber keine konkrete korrigierte SDK-/Build-Nummer, und es wird keine separate Hersteller-Advisory-URL außer dem obigen CVE-Eintrag angegeben. Hier wird keine korrigierte Versionsnummer behauptet, da das Writeup keine bereitstellt.
Eine CometChat-TextMessage trägt ihren Text in data.text, der UI-Code als
message.getText() zur Verfügung gestellt wird. Ein authentifiziertes Mitglied kann an eine Gruppe eine Nachricht senden, deren
data.text HTML-Markup enthält, zum Beispiel:
POST /v3.0/messages
{
"receiverType": "group",
"receiver": "<groupId>",
"category": "message",
"type": "text",
"data": { "text": "" }
}
Die Plattform speichert diese Zeichenkette unverändert. Wenn ein Gruppenmitglied die
Konversation öffnet, nimmt der Render-Pfad der Nachrichtenliste des Clients data.text und fügt
es als HTML, ohne kontextuelle Ausgabe-Kodierung in das DOM ein (ein Schreibvorgang im
innerHTML-Stil statt eines textContent-Schreibvorgangs). Das ``-Markup wird daher geparst;
sein src=x kann nicht geladen werden, was den onerror-Handler auslöst, und das Skript des
Angreifers läuft in der Sitzung des Opfers. Da die Nachricht gespeichert wird, ist dies persistent
und erfordert keine Interaktion des Opfers über das Anzeigen der Gruppe hinaus.
Der offengelegte Fix bereinigt / kodiert dieses Feld kontextuell, sodass dieselbe Eingabe als inert Text gerendert wird, anstatt ausgeführt zu werden.
Ein authentifiziertes Gruppenmitglied ist der Angreifer; die Opfer sind die anderen Mitglieder dieser Gruppe. Die Payload wird einmal zugestellt, serverseitig gespeichert und automatisch im Browser jedes Mitglieds ausgeführt, wenn dieses die Konversation anzeigt. Es ist kein Klick, keine Erteilung einer Berechtigung und keine zusätzliche Interaktion erforderlich. Auswirkung im ursprünglichen Bericht: beliebige Skriptausführung in der Sitzung jedes Mitglieds (Diebstahl von Sitzung/Token, Aktionen im Namen des Opfers, Offenlegung von Konversationsdaten).
repro.html — eine schlichte lokale Seite (einfaches Weiß, minimales CSS, ein einfacher
Nachrichtenlistenbereich), die nur den verwundbaren Render-Pfad des SDK 3.0.12
repräsentiert. Sie enthält eine originalgetreue lokale Kopie der nicht bereinigten Senke:
function getText(message){ return message.data.text; } // mirrors TextMessage#getText()
...
body.innerHTML = getText(message); // *** the SDK 3.0.12 sink, reproduced ***
Sie rendert ein synthetisches gespeichertes Nachrichtenobjekt durch diese Senke, damit das injizierte Skript lokal ausgeführt wird. Ein ehrlicher Hinweis direkt auf der Seite besagt, dass dies eine lokale Reproduktion der SDK-3.0.12-Senke ist, dass das behobene SDK das Feld bereinigt und dass kein Live-CometChat-Dienst beteiligt ist.
payload.json — das synthetische gespeicherte TextMessage-Objekt. Sein data.text-Feld
ist der Injektionspunkt (annotiert mit den Schlüsseln _injection_point / _note, da
JSON keine Kommentare kennt). Der Injektions-Vektor (``) ist
identisch mit dem offengelegten Bericht; die Aktion wurde auf einen harmlosen In-Page-
Marker geändert.
payload.txt — dieselbe Injektion in Klartext dokumentiert, einschließlich der
ursprünglichen manipulierten Anfrage, mit einem ausdrücklichen Hinweis, dass sie nirgendwohin gesendet wird.
Diese repro.html ist ein minimaler Ersatz, geschrieben nach dem Mechanismus aus dem Writeup.
Sie ist kein Auszug aus dem Quellcode des Anbieters; das echte SDK wurde für diese Reproduktion
weder beschafft noch disassembliert.
Die Payload in dieser Repro ist harmlos: Statt alert(document.domain) setzt der
onerror-Handler window.__xss_fired = true, schreibt RESULT: XSS-FIRED in
die Seite und setzt den Dokumenttitel. Es werden keine Daten exfiltriert.
cd poc/CVE-2026-39154
python3 -m http.server 5599
# then open http://localhost:5599/repro.html in a browser
repro.html rendert eine eingebettete Kopie der gespeicherten Nachricht synchron (sodass die Senke auch bei Headless-Capture deterministisch ausgelöst wird) und ruft zusätzlich payload.json ab, um zu bestätigen, dass das Artefakt auf der Festplatte geladen wird und dasselbe Feld enthält.
RESULT: XSS-FIRED in Rot — der Beweis, dass das injizierte onerror-Skript über die nicht bereinigte innerHTML-Senke ausgeführt wurde.data.text kodiert und als wörtlicher Text `` erscheinen, ohne Skriptausführung.payload.json wurde als wohlgeformtes JSON validiert und bestätigt, dass sie die Injektion in data.text enthält (einen -Vektor). Das Verzeichnis wurde mit `python3 -m http.server 5599` bereitgestellt, und `repro.html` wurde in **Firefox 140.11.0esr headless** über `--screenshot` geladen (hier gespeichert als `repro-screenshot.png`). Der aufgezeichnete Screenshot zeigt, dass das als Element für ein defektes Bild gerendert wurde (d. h. data.text wurde als HTML geparst, nicht als Text angezeigt) und RESULT: XSS-FIRED in das DOM geschrieben wurde. Dies bestätigt, dass die nicht bereinigte innerHTML-Senke injiziertes Skript aus dem Nachrichtenfeld ausführt./v3.0/messages gesendet, es wurde kein Konto und keine Gruppe verwendet, und es wurde kein echtes SDK-Build heruntergeladen, ausgeführt oder disassembliert. Die Senke hier ist ein originalgetreuer, handgeschriebener Ersatz auf Basis des im Writeup beschriebenen Mechanismus, nicht der ausgelieferte Code des Anbieters. Die genaue korrigierte SDK-Version wurde nicht bestätigt, da das Writeup keine angibt.