
Scanner de alta fidelidade para CVE-2026-41940 (bypass de autenticação do cPanel & WHM)
Um scanner de alta fidelidade para a bypass de autenticação do cPanel/WHM identificada como CVE-2026-41940. Ele identifica hosts vulneráveis sem produzir os falsos negativos comuns em provas de conceito e detecções públicas, e sem acionar os mecanismos de bloqueio de conta e de lista de permissão de IP raiz que interferem em varreduras ingênuas.
A ferramenta também inclui uma cadeia de exploração separada e opt-in para um bug de path traversal CalDAV da mesma família no cpdavd (portas 2079 sem TLS / 2080 TLS) — CVE-2026-29205 — que permite a um atacante remoto ler arquivos arbitrários como root desde que uma pequena etapa de preparação via SMTP seja bem-sucedida. O cPanel 11.134.0.26 corrige o bug subjacente de ciclo de vida do RAII, então a leitura agora é executada como o proprietário da conta sem privilégios; o path traversal em si ainda alcança o cpdavd, mas não pode escalar além do que essa conta já consegue ler. A cadeia CalDAV é controlada por --exploit e fica desativada por padrão, pois envia e-mails reais e lê arquivos de alvos confirmados — consulte a seção Modo de exploração (ativo).
A maioria das detecções públicas para CVE-2026-41940 compartilha três problemas. Este scanner resolve cada um deles.
Você pode ler nossa publicação no blog sobre esta técnica de detecção aqui: https://slcyber.io/research-center/high-fidelity-check-for-the-cpanel-authentication-bypass-cve-2026-41940/
A configuração do Apache por vhost do cPanel instala um ProxyPass que encaminha /___proxy_subdomain_whm para 127.0.0.1:2086 e /___proxy_subdomain_cpanel para 127.0.0.1:2080 independentemente do cabeçalho Host da requisição. O RewriteCond apenas restringe o rewrite que mapeia o subdomínio de gerenciamento para o caminho de proxy; o ProxyPass em si é incondicional. Acessar esses caminhos em qualquer vhost servido por um Apache gerenciado pelo cPanel alcança o mesmo backend vulnerável das portas de gerenciamento.
Scanners que sondam apenas as portas 2082/2083/2086/2087 relatam um host como não vulnerável quando essas portas estão protegidas por firewall, mesmo que a falha seja totalmente acessível pela 443. Este scanner sonda 2087, 2083 e os dois caminhos de proxy na 443 por padrão.
O cPanel inclui o cphulkd, que bloqueia contas após um pequeno número de tentativas de senha com falha, e o authorized_whm_root_ips, que restringe logins root a uma lista configurada de endereços de origem. Um scanner que explora a bypass tentando injetar uma sessão para root:
Este scanner evita ambos os problemas no lado WHM ao injetar expired=1 na carga útil da sessão sob um nome de usuário gerado aleatoriamente. A injeção de sessão é verificada visitando a URL cpsessXXXX resultante e encontrando msg_code:[expired_session] no corpo da resposta, que só está presente quando a injeção é bem-sucedida. Nenhuma conta real é alvo, portanto nenhuma conta real pode ser bloqueada, e a lista de permissão root é irrelevante porque nenhum login root é tentado.
O daemon do cPanel (cpaneld, portas 2083 e o caminho /___proxy_subdomain_cpanel) exige que o nome de usuário fornecido corresponda a uma conta cPanel existente no disco (-f /var/cpanel/users/$user). Um nome de usuário root nunca satisfará essa verificação porque root é um usuário de sistema, não um usuário do cPanel. Detecções que tentam apenas root produzem falsos negativos nessa superfície. Este scanner usa uma wordlist configurável de nomes de usuário comuns do cPanel contra a superfície do cPanel e recorre ao caminho de nome de usuário aleatório na superfície WHM, que não tem essa restrição.
Para cada alvo, o scanner executa as seguintes etapas por superfície:
GET /login e lê o cabeçalho Set-Cookie tanto para whostmgrsession (WHM) quanto para cpsession (cPanel). O cookie contém um componente de nome de sessão separado por vírgulas.GET / com um cabeçalho Authorization: Basic cujo valor decodificado é <user>:\xff\nexpired=1. O \nexpired=1 final é a carga útil da injeção de sessão. O cookie de sessão da etapa 1 é reproduzido sem modificação.Location da resposta e extrai o token cpsessXXXX.GET /<cpsessXXXX>/ com o cookie original e procura por msg_code:[expired_session] no corpo. Sua presença prova que a injeção de sessão foi bem-sucedida e que o host é vulnerável.No WHM (porta 2087 e o caminho /___proxy_subdomain_whm na 443), o nome de usuário é um u aleatório seguido de dez caracteres hexadecimais. No cPanel (porta 2083 e o caminho /___proxy_subdomain_cpanel na 443), o scanner percorre sua wordlist de nomes de usuário e para na primeira correspondência.
Por padrão, o scanner sonda 2087, 2083 e 443 nessa ordem e para assim que qualquer superfície confirma vulnerabilidade.
--exploit)CVE-2026-29205 — Atualização de Segurança cPanel/WHM WP2, 13 de maio de 2026. Corrigido no cPanel 11.134.0.26. O aviso acompanha a mesma regressão de redução de privilégio do
cpdavdque esta cadeia de exploração abusa.
Descrição completa da falha e da cadeia de exploração: https://slcyber.io/research-center/new-age-of-collisions-reading-arbitrary-files-pre-auth-as-root-in-cpanel-cve-2026-29205
O cpdavd nas portas 2079 (HTTP puro) e 2080 (TLS) confia no caminho <principal>/<collection>/... que ele constrói ao servir recursos CalDAV/CardDAV. Ao criar uma requisição cujo componente de caminho codifica segmentos .. e apontá-la para uma pasta maildir cujo nome em disco também codifica a travessia (x-attachment-1-y), o cpdavd pode ser coagido a ler qualquer arquivo no disco como root, independentemente de propriedade ou permissões — incluindo /etc/shadow, /etc/passwd e os spools de e-mail por usuário.
A defesa em profundidade que deveria reduzir os privilégios para o proprietário da conta antes da leitura falhou silenciosamente: o objeto Cpanel::AccessIds::ReducedPrivileges foi construído em contexto void, então seu destrutor restaurou os privilégios de root antes que a leitura fosse executada. O cPanel 11.134.0.26 vincula o objeto a uma variável lexical my $privs, para que ele viva por toda a cadeia -f / stat / open / read; em hosts corrigidos, a leitura é executada como o proprietário da conta sem privilégios em vez de root.
A pasta vulnerável precisa existir em disco antes que a leitura funcione. O cPanel cria automaticamente uma pasta chamada .x-attachment-1-y para o destinatário <user>+x-attachment-1-y@<domain> na primeira vez que um e-mail chega nesse sub-endereço. A cadeia é, portanto:
info, admin, webmaster).<prefix>+x-attachment-1-y@<domain> para cada candidato. Respostas RCPT TO aceitas são registradas.GET de path traversal contra cpdavd nas portas 2080 (TLS) e 2079 (pura), sob os prefixos de coleção /calendar/ e /addressbook/.Um sucesso retorna os bytes do arquivo; o achado registra o e-mail usado, a coleção, a contagem de bytes e os primeiros 200 bytes como pré-visualização.
Esta verificação fica desativada a menos que --exploit seja passado. Quando ativada, exige um relay SMTP de saída funcional (veja Arquivo de configuração abaixo), pois a etapa de criação da pasta não pode ser ignorada.
A exploração só funciona contra contas virtuais de e-mail reais configuradas no recurso Email Accounts do cPanel. Endereços catch-all não funcionam — um catch-all é roteado via roteador system_aliases do Exim, nunca chega ao transporte dovecot_virtual_delivery e, portanto, nunca aciona o caminho lda_mailbox_autocreate que produz a pasta .x-attachment-1-y/.
Quando você já conhece um e-mail virtual válido no alvo, passe-o com --email:
python scanner.py --config scanner.ini --email [email protected] target.com
--email ignora completamente a enumeração de SANs do certificado e a wordlist de prefixos e envia exatamente uma mensagem para o endereço que você forneceu. Ele pode ser repetido para atingir várias contas conhecidas. O caminho direcionado é muito mais confiável que o caminho por spray.
Sem --email, o scanner recorre ao spray de ~15 prefixos comuns (info, admin, webmaster, etc.) por domínio extraído do certificado TLS do host. A medição da Assetnote em uma amostra de 200 hosts expostos via cpdavd foi uma taxa de acerto por spray de ~10%: um resultado limpo do modo spray é uma evidência fraca de que o host está corrigido, e reexecutar com --email contra uma conta real é a única maneira confiável de confirmar.
O modo de exploração lê um arquivo INI via --config:
python scanner.py --config scanner.ini --exploit example.com
Copie scanner.ini.example para scanner.ini, preencha as credenciais SMTP e, opcionalmente, ajuste os padrões do CalDAV. O scanner.ini está no .gitignore para que a cópia preenchida permaneça local. A senha SMTP também pode ser fornecida pela variável de ambiente SCANNER_SMTP_PASSWORD, que só tem precedência quando o campo password no arquivo está vazio.
pip install -r requirements.txt
É necessário Python 3.8 ou posterior.
Alvo único:
python scanner.py example.com
Vários alvos via argumentos posicionais:
python scanner.py host-a.example.com host-b.example.com:2083
Um arquivo de alvos, um por linha. Linhas começando com # são ignoradas:
python scanner.py -f targets.txt
Lendo alvos da entrada padrão (stdin):
cat targets.txt | python scanner.py
Um alvo pode ser um nome de host ou host:port. Quando uma porta é especificada, o scanner sonda apenas essa porta; caso contrário, ele sonda 2087, 2083 e 443.
-u, --users — nomes de usuário do cPanel separados por vírgula para tentar na
superfície do cPanel. O padrão é uma pequena lista embutida.-U, --users-file — arquivo com um nome de usuário do cPanel por linha.-p, --ports — portas separadas por vírgula para sondar quando nenhuma porta é especificada no
alvo. O padrão é 2087,2083,443.-t, --threads — threads por alvo usadas para percorrer a lista de nomes de usuário na
superfície do cPanel. O padrão é 10.-c, --concurrency — número de alvos escaneados em paralelo. O padrão é 20.-T, --timeout — tempo limite por requisição em segundos. O padrão é 15.-o, --output — anexa alvos vulneráveis, um por linha, a este arquivo à
medida que são descobertos.--json — grava um registro JSON Lines por alvo neste arquivo.-q, --quiet — imprime apenas alvos vulneráveis no stdout. Falhas de conexão
e alvos limpos ainda são registrados em --json e contados no resumo.Uma linha é gravada no stdout por achado, portanto um alvo executando as duas verificações imprimirá duas vezes:
[!] host cve-2026-41940 VULNERABLE (port 443)
[!] host caldav-traversal VULNERABLE via admin@host (read 1842b from /etc/passwd)
[+] host cve-2026-41940 NOT VULNERABLE
[?] host cve-2026-41940 CONNECTION FAILED
A saída --json é um registro por alvo com um array findings:
{"target": "host", "status": "VULNERABLE", "findings": [
{"check": "cve-2026-41940", "status": "VULNERABLE", "detail": {"port": 443}},
{"check": "caldav-traversal", "status": "VULNERABLE",
"detail": {"email": "admin@host", "domain": "host", "collection": "calendar",
"file": "/etc/shadow", "bytes": 1218, "preview": "root:$6$..."}}
]}
O status no nível superior é o pior caso entre todos os achados.
Uma linha de resumo com totais é gravada no stderr ao final. A barra de progresso é renderizada no stderr e é suprimida automaticamente quando o stderr não é um terminal.
O código de saída é 0 se qualquer alvo for vulnerável, 1 se todos os alvos acessíveis estiverem limpos e 2 se nenhum alvo puder ser alcançado.
Escaneie uma lista de alvos, grave os acertos em um arquivo e permaneça silencioso no stdout:
python scanner.py -f targets.txt -o vulnerable.txt -q
Escaneie com uma lista personalizada de nomes de usuário contra a superfície do cPanel, paralelismo aumentado e saída JSON para processamento downstream:
python scanner.py -f targets.txt -U cpanel-users.txt -c 100 --json results.jsonl
Sonde um conjunto de portas não padrão:
python scanner.py -p 2083,2087,8443 -f targets.txt
Execute a cadeia CalDAV de ponta a ponta contra um único alvo com o fallback de spray de e-mail (exige um scanner.ini preenchido):
python scanner.py --config scanner.ini --exploit example.com
Exploração direcionada contra um e-mail virtual conhecido — taxa de acerto muito maior que a do spray:
python scanner.py --config scanner.ini --email [email protected] example.com
Execute apenas a cadeia CalDAV contra uma lista de hosts cpdavd confirmados, despejando prévias de /etc/passwd em JSON:
python scanner.py --config scanner.ini --caldav-only \
-f cpdavd-hosts.txt --json caldav-results.jsonl
A invocação padrão scanner.py <target> executa apenas o detector do CVE-2026-41940. Ela envia as requisições necessárias para confirmar a injeção de sessão e nada mais. Não faz login como nenhum usuário real, não tem como alvo a conta root, não eleva privilégios para um shell e não acumula eventos de senha com falha contra nenhuma conta válida em um sistema alvo. O marcador que ela encontra (msg_code:[expired_session]) é gerado pelo próprio aplicativo em resposta ao campo de sessão expired=1 injetado e é o mesmo indicador que a página de login do cPanel upstream usa quando uma sessão legitimamente expirada é reproduzida.
Passar --exploit (ou --caldav-only) desbloqueia a cadeia de path traversal CalDAV. Isso não é mais um detector — é uma exploração funcional. Quando ativado, o scanner, para cada alvo em que a enumeração de domínio for bem-sucedida:
scanner.ini e enviar uma mensagem curta por destinatário candidato (normalmente 10–15 por domínio, 3 domínios por alvo);--read-file configurado de todos os alvos confirmados.O arquivo padrão é /etc/shadow. Uma leitura bem-sucedida retorna os bytes do arquivo; um corpo vazio indica um alvo inacessível ou um host onde a correção de redução de privilégio (cPanel 11.134.0.26, my $privs = …) está aplicada. A detecção compara o comprimento do corpo recebido com zero, não com o Content-Length anunciado na resposta (que é derivado de stat() no caminho escolhido pelo atacante e será preenchido mesmo quando o open() subsequente for negado).
Para testar o alcance do path traversal independentemente da correção de redução de privilégio — por exemplo, quando você quer saber se cpdavd está acessível e se o pré-requisito maildir foi satisfeito em um host corrigido — execute novamente com --read-file /etc/passwd. O /etc/passwd é legível por todos, portanto retorna bytes tanto em hosts pré-correção quanto pós-correção; combinar os dois sinais (corpo de /etc/shadow presente = leitura root pré-correção; /etc/shadow vazio + /etc/passwd presente = path traversal acessível, mas correção de redução de privilégio aplicada) classifica um host sem ambiguidade.
O conteúdo e uma prévia de 200 bytes são gravados na saída JSONL e impressos no stdout. A exploração CalDAV por domínio pode levar um minuto ou mais — isso é a escada de repetição aguardando a entrega de e-mail, não um travamento.
Execute --exploit apenas contra ativos que você possui ou para os quais tem autorização escrita explícita para testar. O tráfego SMTP é registrado pelo relay configurado e por todos os sistemas de e-mail destinatários; as leituras de arquivo são registradas pelo cpdavd.
--no-progress — desativa a barra de progresso.--exploit — ativa a cadeia de path traversal CalDAV. Desativada por padrão;
veja Modo de exploração (ativo) para os efeitos colaterais que esta
flag desbloqueia.--config — arquivo INI com as credenciais do relay SMTP e ajustes do CalDAV.
Veja scanner.ini.example.--read-file — arquivo a ser exfiltrado quando a cadeia CalDAV for bem-sucedida.
Substitui o valor do arquivo de configuração. O padrão é /etc/shadow — um
arquivo somente-root, portanto uma leitura bem-sucedida distingue pré-correção (retorna
o conteúdo do shadow) de pós-correção (open negado, corpo vazio → relatado
NOT VULNERABLE). Use --read-file /etc/passwd para testar o alcance do path traversal
sem distinguir corrigido/não corrigido.--caldav-only — ignora a verificação do 41940 e executa apenas a cadeia CalDAV.
Implica --exploit. Útil para reexecutar a cadeia contra uma lista de alvos
já conhecidos como acessíveis via CalDAV.--email ADDR — conta de e-mail virtual conhecida no alvo. Ignora a enumeração de SANs do
certificado e a wordlist de spray; envia exatamente uma mensagem para ADDR e
lê contra esse principal. Pode ser repetido. Implica --exploit. Veja
Exploração direcionada vs. por spray.-v, --verbose — emite o progresso por domínio da cadeia CalDAV
(lista de SANs do certificado, contagem de spray, escada de repetição).