
CVE-2026-39154, XSS memorizzato in CometChat JS SDK
Questa directory contiene una riproduzione onesta e autonoma del render sink
alla base di CVE-2026-39154: un difetto di cross-site scripting memorizzato nel
JavaScript SDK 3.0.12 di CometChat, in cui il campo data.text di un messaggio di gruppo viene renderizzato nei
client degli altri membri senza codifica contestuale dell'output.
Funziona interamente in locale con dati sintetici. Non contatta, sonda né si autentica al servizio CometChat live o a terze parti. È una dimostrazione locale del solo percorso di render del client vulnerabile.
Repository PoC corrispondente (citato nel writeup): https://github.com/defineid/Wildfire
Writeup: writeup-cometchat-xss.html in questo portfolio.
| Campo | Valore |
|---|---|
| CVE | CVE-2026-39154 |
| Vendor / Prodotto | CometChat · JavaScript SDK 3.0.12 |
| Classe | Cross-Site Scripting memorizzato (CWE-79) |
| Endpoint | POST /v3.0/messages · campo di iniezione data.text |
| Destinatario | receiverType: group |
| Interazione | Nessuna; il payload memorizzato si esegue automaticamente quando un membro apre il gruppo |
| Divulgazione | Segnalato a CometChat a febbraio 2026 (richiesta 42660); il vendor ha contrassegnato Solved; CVE assegnato da MITRE |
| Riferimento | https://www.cve.org/CVERecord?id=CVE-2026-39154 |
Nota sulla versione corretta: il writeup pubblicato afferma che il vendor ha risolto il problema ("VENDOR: SOLVED") ma non indica un numero specifico di SDK/build corretto, e non viene fornito alcun URL di advisory del vendor separato oltre al record CVE sopra riportato. Nessun numero di versione corretta viene affermato qui perché il writeup non ne fornisce uno.
Un TextMessage di CometChat trasporta il proprio testo in data.text, esposto al codice UI come
message.getText(). Un membro autenticato può inviare, a un gruppo, un messaggio il cui
data.text contiene markup HTML, ad esempio:
POST /v3.0/messages
{
"receiverType": "group",
"receiver": "<groupId>",
"category": "message",
"type": "text",
"data": { "text": "" }
}
La piattaforma memorizza quella stringa così com'è. Quando un qualsiasi membro del gruppo apre la conversazione, il percorso di rendering della lista dei messaggi del client prende data.text e lo inserisce nel DOM come HTML, senza codifica contestuale dell'output (una scrittura in stile innerHTML anziché una scrittura textContent). Il markup `` viene quindi analizzato; il suo src=x non riesce a caricarsi, il che attiva il gestore onerror, e lo script dell'attaccante viene eseguito nella sessione della vittima. Poiché il messaggio è memorizzato, questo è persistente e non richiede alcuna interazione della vittima oltre alla visualizzazione del gruppo.
La correzione divulgata sanifica / codifica contestualmente questo campo, così lo stesso input viene renderizzato come testo inerte anziché essere eseguito.
Un membro autenticato del gruppo è l'attaccante; le vittime sono gli altri membri di quel gruppo. Il payload viene consegnato una volta, memorizzato lato server ed eseguito automaticamente nel browser di ogni membro quando visualizza la conversazione. Non è richiesto alcun clic, concessione di permessi o interazione aggiuntiva. Impatto nel report originale: esecuzione arbitraria di script nella sessione di ciascun membro (furto di sessione/token, azioni per conto della vittima, divulgazione dei dati della conversazione).
repro.html — una pagina locale essenziale (bianco semplice, CSS minimale, una semplice area per la lista dei messaggi) che rappresenta solo il percorso di rendering vulnerabile dell'SDK 3.0.12. Contiene una copia locale fedele del sink non sanificato:
function getText(message){ return message.data.text; } // mirrors TextMessage#getText()
...
body.innerHTML = getText(message); // *** the SDK 3.0.12 sink, reproduced ***
Questo rende un oggetto sintetico di messaggio memorizzato attraverso quel sink, così lo script iniettato viene eseguito localmente. Una nota onesta inline nella pagina afferma che si tratta di una riproduzione locale del sink dell'SDK 3.0.12, che l'SDK corretto sanifica il campo, e che nessun servizio CometChat live è coinvolto.
payload.json — l'oggetto sintetico memorizzato TextMessage. Il suo campo data.text
è il punto di iniezione (annotato con le chiavi _injection_point / _note, poiché
JSON non ha commenti). Il vettore di iniezione (``) è
identico al report divulgato; l'azione è modificata in un marcatore innocuo
all'interno della pagina.
payload.txt — la stessa iniezione documentata in testo semplice, inclusa la
richiesta originale appositamente costruita, con una nota esplicita che non viene inviata da nessuna parte.
Questo repro.html è un sostituto minimale scritto a partire dal meccanismo descritto nel writeup.
Non è un estratto del codice sorgente del fornitore; l'SDK reale non è stato ottenuto
né disassemblato per questa riproduzione.
Il payload in questa repro è benigno: invece di alert(document.domain), il
gestore onerror imposta window.__xss_fired = true, scrive RESULT: XSS-FIRED nella
pagina e imposta il titolo del documento. Nulla viene esfiltrato.
cd poc/CVE-2026-39154
python3 -m http.server 5599
# then open http://localhost:5599/repro.html in a browser
repro.html esegue il rendering di una copia incorporata del messaggio memorizzato in modo sincrono (così il sink si attiva in modo deterministico anche in cattura headless) e recupera inoltre payload.json per confermare che l'artefatto su disco si carichi e contenga lo stesso campo.
RESULT: XSS-FIRED in rosso — prova che lo script onerror iniettato è stato eseguito tramite il sink innerHTML non sanificato.data.text verrebbe codificato e apparirebbe come il testo letterale `` senza alcuna esecuzione di script.payload.json è stato validato come JSON ben formato e confermato contenere l'iniezione in data.text (un vettore ). La directory è stata servita con `python3 -m http.server 5599`, e `repro.html` è stato caricato in **Firefox 140.11.0esr headless** tramite `--screenshot` (salvato qui come `repro-screenshot.png`). Lo screenshot catturato mostra l' renderizzato come elemento immagine non caricata (ovvero data.text è stato interpretato come HTML, non mostrato come testo) e RESULT: XSS-FIRED scritto nel DOM. Ciò conferma che il sink innerHTML non sanificato esegue lo script iniettato dal campo del messaggio./v3.0/messages, nessun account o gruppo è stato utilizzato, e nessuna build reale dell'SDK è stata scaricata, eseguita o disassemblata. Il sink qui presente è un sostituto fedele scritto a mano basato sul meccanismo descritto nel writeup, non sul codice distribuito dal fornitore. La versione esatta dell'SDK corretto non è stata confermata perché il writeup non ne indica alcuna.