
Escáner de alta fidelidad para CVE-2026-41940 (bypass de autenticación de cPanel y WHM)
Un escáner de alta fidelidad para la omisión de autenticación de cPanel/WHM rastreada como CVE-2026-41940. Identifica hosts vulnerables sin producir los falsos negativos comunes en las pruebas de concepto y detecciones públicas, y sin activar el bloqueo de cuenta ni los mecanismos de lista de permisos de IP raíz que interfieren con el escaneo ingenuo.
La herramienta también incluye una cadena de explotación opcional e independiente para una vulnerabilidad de recorrido de rutas CalDAV de la misma familia en cpdavd (puertos 2079 plano / 2080 TLS) — CVE-2026-29205 — que permite a un atacante remoto leer archivos arbitrarios como root una vez que un pequeño paso de configuración impulsado por SMTP tiene éxito. cPanel 11.134.0.26 corrige el error subyacente de ciclo de vida de RAII, por lo que la lectura ahora se ejecuta como el propietario de la cuenta sin privilegios; el recorrido en sí todavía llega a cpdavd pero no puede escalar más allá de lo que esa cuenta ya puede leer. La cadena CalDAV está controlada por --exploit y está desactivada por defecto porque envía correos electrónicos reales y lee archivos de objetivos confirmados — consulte la sección Modo de explotación (activo).
La mayoría de las detecciones públicas para CVE-2026-41940 comparten tres problemas. Este escáner aborda cada uno de ellos.
Puede leer nuestra publicación de blog sobre esta técnica de detección aquí: https://slcyber.io/research-center/high-fidelity-check-for-the-cpanel-authentication-bypass-cve-2026-41940/
La configuración de Apache por vhost de cPanel instala un ProxyPass que reenvía /___proxy_subdomain_whm a 127.0.0.1:2086 y /___proxy_subdomain_cpanel a 127.0.0.1:2080 independientemente del encabezado Host de la solicitud. La RewriteCond solo restringe la reescritura que asigna el subdominio de administración a la ruta de proxy; el ProxyPass en sí es incondicional. Al acceder a estas rutas en cualquier vhost servido por un Apache administrado por cPanel se llega al mismo backend vulnerable que con los puertos de administración.
Los escáneres que solo sondean los puertos 2082/2083/2086/2087 reportarán un host como no vulnerable cuando esos puertos están protegidos por firewall, aunque el error sea totalmente accesible a través de 443. Este escáner sondea 2087, 2083 y las dos rutas de proxy en 443 por defecto.
cPanel incluye cphulkd, que bloquea cuentas después de un pequeño número de intentos de contraseña fallidos, y authorized_whm_root_ips, que restringe los inicios de sesión de root a una lista configurada de direcciones de origen. Un escáner que explota la omisión intentando inyectar una sesión para root:
Este escáner evita ambos problemas en el lado de WHM inyectando expired=1 en la carga útil de la sesión bajo un nombre de usuario generado aleatoriamente. La inyección de sesión se verifica visitando la URL cpsessXXXX resultante y buscando msg_code:[expired_session] en el cuerpo de la respuesta, que solo está presente cuando la inyección tuvo éxito. No se ataca ninguna cuenta real, por lo que ninguna cuenta real puede ser bloqueada, y la lista de permisos de root es irrelevante porque no se intenta ningún inicio de sesión de root.
El demonio de cPanel (cpaneld, puertos 2083 y la ruta /___proxy_subdomain_cpanel) requiere que el nombre de usuario proporcionado corresponda a una cuenta de cPanel existente en el disco (-f /var/cpanel/users/$user). Un nombre de usuario de root nunca satisfará esta comprobación porque root es un usuario del sistema, no un usuario de cPanel. Las detecciones que solo prueban root producen falsos negativos en esta superficie. Este escáner utiliza una lista de palabras configurable de nombres de usuario comunes de cPanel contra la superficie de cPanel y recurre a la ruta de nombre de usuario aleatorio en la superficie de WHM, que no tiene tal restricción.
Para cada objetivo, el escáner realiza los siguientes pasos por superficie:
GET /login y lee el encabezado Set-Cookie para whostmgrsession (WHM) o cpsession (cPanel). La cookie contiene un componente de nombre de sesión separado por comas.GET / con un encabezado Authorization: Basic cuyo valor decodificado es <user>:\xff\nexpired=1. El \nexpired=1 final es la carga útil de inyección de sesión. La cookie de sesión del paso 1 se reproduce sin modificaciones.Location de la respuesta y extrae el token cpsessXXXX.GET /<cpsessXXXX>/ con la cookie original y busca msg_code:[expired_session] en el cuerpo. Su presencia demuestra que la inyección de sesión tuvo éxito y que el host es vulnerable.En WHM (puerto 2087 y la ruta /___proxy_subdomain_whm en 443) el nombre de usuario es una u aleatoria seguida de diez caracteres hexadecimales. En cPanel (puerto 2083 y la ruta /___proxy_subdomain_cpanel en 443) el escáner recorre su lista de palabras de nombres de usuario y se detiene en la primera coincidencia.
Por defecto, el escáner sondea 2087, 2083 y 443 en ese orden y se detiene en cuanto cualquier superficie confirma la vulnerabilidad.
--exploit)CVE-2026-29205 — Actualización de seguridad WP2 de cPanel/WHM, 13 de mayo de 2026. Corregido en cPanel 11.134.0.26. El aviso rastrea la misma regresión de caída de privilegios de
cpdavdque esta cadena de explotación aprovecha.
Informe completo del error y la cadena de explotación: https://slcyber.io/research-center/new-age-of-collisions-reading-arbitrary-files-pre-auth-as-root-in-cpanel-cve-2026-29205
cpdavd en los puertos 2079 (HTTP plano) y 2080 (TLS) confía en la ruta <principal>/<collection>/... que construye al servir recursos CalDAV/CardDAV. Al crear una solicitud cuyo componente de ruta codifica segmentos .. y apuntarla a una carpeta maildir cuyo nombre en disco también codifica el recorrido (x-attachment-1-y), se puede obligar a cpdavd a leer cualquier archivo en el disco como root, independientemente de la propiedad o los permisos — incluidos /etc/shadow, /etc/passwd y los buzones de correo de cada usuario.
La defensa en profundidad que se suponía que debía reducir los privilegios al propietario de la cuenta antes de la lectura falló silenciosamente: el objeto Cpanel::AccessIds::ReducedPrivileges se construyó en contexto void, por lo que su destructor restauró los privilegios de root antes de que la lectura se ejecutara. cPanel 11.134.0.26 vincula el objeto a un my $privs léxico para que viva a través de la cadena -f / stat / open / read; en hosts parcheados, la lectura se ejecuta por tanto como el propietario de la cuenta sin privilegios en lugar de root.
La carpeta vulnerable debe existir en el disco antes de que la lectura funcione. cPanel crea automáticamente una carpeta llamada .x-attachment-1-y para el destinatario <user>+x-attachment-1-y@<domain> la primera vez que un correo electrónico llega a esa subdirección. La cadena es, por tanto:
info, admin, webmaster).<prefix>+x-attachment-1-y@<domain> a cada candidato. Se rastrean las respuestas RCPT TO aceptadas.GET de recorrido de rutas contra cpdavd en los puertos 2080 (TLS) y 2079 (plano), bajo ambos prefijos de colección /calendar/ y /addressbook/.Un éxito devuelve los bytes del archivo; el hallazgo registra el correo electrónico utilizado, la colección, el recuento de bytes y los primeros 200 bytes como vista previa.
Esta comprobación está desactivada a menos que se pase --exploit. Cuando está habilitada, requiere un relé SMTP de salida funcional (consulte Archivo de configuración a continuación) porque el paso de creación de carpeta no se puede omitir.
La explotación solo funciona contra cuentas de correo electrónico virtuales reales configuradas bajo la función Email Accounts de cPanel. Las direcciones catch-all no funcionan — una catch-all se enruta a través del router system_aliases de Exim, nunca llega al transporte dovecot_virtual_delivery y, por tanto, nunca activa la ruta lda_mailbox_autocreate que produce la carpeta .x-attachment-1-y/.
Cuando ya conoce un correo electrónico virtual válido en el objetivo, páselo con --email:
python scanner.py --config scanner.ini --email [email protected] target.com
--email omite por completo la enumeración de SAN del certificado y la lista de palabras de prefijos, y envía exactamente un mensaje a la dirección que proporcionó. Puede repetirse para atacar múltiples cuentas conocidas. La ruta dirigida es mucho más fiable que la ruta de rociado.
Sin --email, el escáner recurre a rociar ~15 prefijos comunes (info, admin, webmaster, etc.) por dominio extraído del certificado TLS del host. La medición de Assetnote en una muestra de 200 hosts expuestos a cpdavd fue una tasa de aciertos de rociado de ~10%: un resultado limpio del modo de rociado es una evidencia débil de que el host está parcheado, y volver a ejecutar con --email contra una cuenta real es la única forma fiable de confirmarlo.
El modo de explotación lee un archivo INI a través de --config:
python scanner.py --config scanner.ini --exploit example.com
Copie scanner.ini.example a scanner.ini, complete las credenciales SMTP y, opcionalmente, ajuste los valores predeterminados de CalDAV. scanner.ini está en .gitignore para que la copia completada permanezca local. La contraseña SMTP también puede proporcionarse a través de la variable de entorno SCANNER_SMTP_PASSWORD, que tiene prioridad solo cuando el campo password del archivo está vacío.
pip install -r requirements.txt
Se requiere Python 3.8 o posterior.
Objetivo único:
python scanner.py example.com
Múltiples objetivos mediante argumentos posicionales:
python scanner.py host-a.example.com host-b.example.com:2083
Un archivo de objetivos, uno por línea. Las líneas que comienzan con # se ignoran:
python scanner.py -f targets.txt
Lectura de objetivos desde stdin:
cat targets.txt | python scanner.py
Un objetivo puede ser un nombre de host o host:puerto. Cuando se especifica un puerto, el escáner solo sondea ese puerto; de lo contrario, sondea 2087, 2083 y 443.
-u, --users — nombres de usuario de cPanel separados por comas para probar en la superficie de cPanel. Por defecto, una pequeña lista incorporada.-U, --users-file — archivo con un nombre de usuario de cPanel por línea.-p, --ports — puertos separados por comas para sondear cuando no se especifica ningún puerto en el objetivo. Por defecto, 2087,2083,443.-t, --threads — hilos por objetivo utilizados para recorrer la lista de nombres de usuario contra la superficie de cPanel. Por defecto, 10.-c, --concurrency — número de objetivos escaneados en paralelo. Por defecto, 20.-T, --timeout — tiempo de espera por solicitud en segundos. Por defecto, 15.-o, --output — añade objetivos vulnerables, uno por línea, a este archivo a medida que se descubren.--json — escribe un registro JSON Lines por objetivo en este archivo.-q, --quiet — solo imprime objetivos vulnerables en stdout. Los fallos de conexión y los objetivos limpios todavía se registran en --json y se cuentan en el resumen.Se escribe una línea en stdout por hallazgo, por lo que un objetivo que ejecuta ambas comprobaciones se imprimirá dos veces:
[!] 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
La salida --json es un registro por objetivo con una matriz 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$..."}}
]}
status en el nivel superior es el peor caso entre todos los hallazgos.
Una línea de resumen con totales se escribe en stderr al final. La barra de progreso se representa en stderr y se suprime automáticamente cuando stderr no es una terminal.
El código de salida es 0 si algún objetivo es vulnerable, 1 si todos los objetivos alcanzables estaban limpios y 2 si no se pudo alcanzar ningún objetivo.
Escanear una lista de objetivos, escribir los aciertos en un archivo y permanecer en silencio en stdout:
python scanner.py -f targets.txt -o vulnerable.txt -q
Escanear con una lista de nombres de usuario personalizada contra la superficie de cPanel, mayor paralelismo y salida JSON para procesamiento posterior:
python scanner.py -f targets.txt -U cpanel-users.txt -c 100 --json results.jsonl
Sondear un conjunto de puertos no predeterminado:
python scanner.py -p 2083,2087,8443 -f targets.txt
Ejecutar la cadena CalDAV de extremo a extremo contra un solo objetivo con el respaldo de rociado de correo electrónico (requiere un scanner.ini completado):
python scanner.py --config scanner.ini --exploit example.com
Explotación dirigida contra un correo electrónico virtual conocido — tasa de aciertos mucho más alta que el rociado:
python scanner.py --config scanner.ini --email [email protected] example.com
Ejecutar solo la cadena CalDAV contra una lista de hosts cpdavd confirmados, volcando vistas previas de /etc/passwd a JSON:
python scanner.py --config scanner.ini --caldav-only \
-f cpdavd-hosts.txt --json caldav-results.jsonl
La invocación predeterminada scanner.py <target> solo ejecuta el detector de CVE-2026-41940. Envía las solicitudes necesarias para confirmar la inyección de sesión y nada más. No inicia sesión como ningún usuario real, no ataca la cuenta root, no escala a un shell y no acumula eventos de contraseña fallida contra ninguna cuenta válida en un sistema objetivo. El marcador que busca (msg_code:[expired_session]) es generado por la propia aplicación en respuesta al campo de sesión inyectado expired=1 y es el mismo indicador que la página de inicio de sesión de cPanel utiliza cuando se reproduce una sesión legítimamente caducada.
Pasar --exploit (o --caldav-only) desbloquea la cadena de recorrido de rutas CalDAV. Esto ya no es un detector — es una explotación funcional. Cuando está habilitado, el escáner, para cada objetivo donde la enumeración de dominios tiene éxito:
scanner.ini y enviará un mensaje corto por destinatario candidato (normalmente 10–15 por dominio, 3 dominios por objetivo);--read-file configurado de cada objetivo confirmado.El archivo predeterminado es /etc/shadow. Una lectura exitosa devuelve los bytes del archivo; un cuerpo vacío indica un objetivo inalcanzable o un host donde la corrección de caída de privilegios (cPanel 11.134.0.26, my $privs = …) está en vigor. La detección compara la longitud del cuerpo recibido con cero, no con el Content-Length anunciado en la respuesta (que se deriva de stat() en la ruta elegida por el atacante y estará poblado incluso cuando el open() posterior se deniegue).
Para probar la alcanzabilidad del recorrido independientemente de la corrección de caída de privilegios — por ejemplo, cuando desea saber si cpdavd es alcanzable y el requisito previo de maildir se cumplió en un host parcheado — vuelva a ejecutar con --read-file /etc/passwd. /etc/passwd es legible por todos, por lo que devuelve bytes tanto en hosts pre-parche como post-parche; combinar las dos señales (/etc/shadow con cuerpo presente = lectura root pre-parche; /etc/shadow vacío + /etc/passwd presente = recorrido alcanzable pero con la corrección de caída de privilegios aplicada) clasifica un host sin ambigüedad.
Los contenidos y una vista previa de 200 bytes se escriben en la salida JSONL y se imprimen en stdout. La explotación CalDAV por dominio puede tardar un minuto o más — esta es la escalera de reintentos esperando la entrega del correo, no un cuelgue.
Solo ejecute --exploit contra activos que posee o para los que tiene autorización escrita explícita para probar. El tráfico SMTP es registrado por el relé configurado y por cada sistema de correo del destinatario; las lecturas de archivos son registradas por cpdavd.
--no-progress — desactiva la barra de progreso.--exploit — habilita la cadena de recorrido de rutas CalDAV. Desactivada por defecto; consulte Modo de explotación (activo) para los efectos secundarios que esta bandera desbloquea.--config — archivo INI con las credenciales del relé SMTP y los parámetros ajustables de CalDAV. Consulte scanner.ini.example.--read-file — archivo a exfiltrar cuando la cadena CalDAV tiene éxito. Anula el valor en el archivo de configuración. Por defecto, /etc/shadow — un archivo solo de root, por lo que una lectura exitosa distingue la pre-parche (devuelve contenido de shadow) de la post-parche (open denegado, cuerpo vacío → reportado como NO VULNERABLE). Use --read-file /etc/passwd para probar la alcanzabilidad del recorrido sin distinguir parcheado/no parcheado.--caldav-only — omite la comprobación de 41940 y solo ejecuta la cadena CalDAV. Implica --exploit. Útil para volver a ejecutar la cadena contra una lista de objetivos ya conocidos como alcanzables por CalDAV.--email ADDR — cuenta de correo electrónico virtual conocida en el objetivo. Omite la enumeración de SAN del certificado y la lista de palabras de rociado; envía exactamente un mensaje a ADDR y lee contra ese principal. Puede repetirse. Implica --exploit. Consulte Explotación dirigida vs. rociado.-v, --verbose — emite el progreso por dominio para la cadena CalDAV (lista de SAN del certificado, recuento de rociado, escalera de reintentos).