
CVE-2026-41940(cPanel 和 WHM 认证绕过)的高保真扫描器
一个用于跟踪的cPanel/WHM认证绕过漏洞 CVE-2026-41940 的高保真扫描器。它能识别易受攻击的主机,而不会产生公共PoC和检测中常见的假阴性,也不会触发干扰简单扫描的账户锁定和根IP白名单机制。
该工具还附带了一个独立的、可选利用链,用于同一家族的cpdavd上的CalDAV路径遍历漏洞(端口2079明文/2080 TLS)—— CVE-2026-29205 —— 一旦一个小的SMTP驱动设置步骤成功,远程攻击者就可以以root身份读取任意文件。cPanel 11.134.0.26 修复了底层的RAII生命周期错误,因此读取现在以非特权账户所有者身份运行;遍历本身仍然可以到达cpdavd,但无法超越该账户已能读取的范围。CalDAV链通过--exploit启用,并且默认关闭,因为它会发送真实邮件并从确认的目标读取文件——请参阅利用模式(活跃)部分。
大多数针对CVE-2026-41940的公共检测都存在三个问题。这个扫描器逐一解决了它们。
您可以在此处阅读我们关于此检测技术的博客文章:https://slcyber.io/research-center/high-fidelity-check-for-the-cpanel-authentication-bypass-cve-2026-41940/
cPanel的每个虚拟主机Apache配置安装了一个ProxyPass,将/___proxy_subdomain_whm转发到127.0.0.1:2086,将/___proxy_subdomain_cpanel转发到127.0.0.1:2080,而不考虑请求的Host头。RewriteCond仅约束将管理子域映射到代理路径的重写;ProxyPass本身是无条件的。在cPanel管理的Apache服务的任何虚拟主机上访问这些路径,都会到达与管理端口相同的有漏洞后端。
只探测端口2082/2083/2086/2087的扫描器,当这些端口被防火墙阻止时,会报告主机不易受攻击,尽管漏洞完全可以通过443端口访问。此扫描器默认探测2087、2083以及443上的两个代理路径。
cPanel内置了cphulkd,它会在少量密码尝试失败后锁定账户,以及authorized_whm_root_ips,它将root登录限制为配置的源地址列表。一个试图通过为root注入会话来利用绕过漏洞的扫描器将:
此扫描器通过在WHM端将expired=1注入到会话负载中,并使用随机生成的用户名,从而避免了这两个问题。会话注入通过访问生成的cpsessXXXX URL并匹配响应体中的msg_code:[expired_session]来验证,该字段仅在注入成功时出现。没有针对真实账户,因此没有真实账户会被锁定,并且根白名单无关紧要,因为没有尝试root登录。
cPanel守护进程(cpaneld,端口2083和/___proxy_subdomain_cpanel路径)要求提供的用户名对应于磁盘上现存的cPanel账户(-f /var/cpanel/users/$user)。用户名root永远无法满足此检查,因为root是系统用户,不是cPanel用户。只尝试root的检测在此界面上会产生假阴性。此扫描器针对cPanel界面使用可配置的常见cPanel用户名单词表,并在WHM界面(没有此限制)回退到随机用户名路径。
对于每个目标,扫描器对每个界面执行以下步骤:
GET /login并读取Set-Cookie头中的whostmgrsession(WHM)或cpsession(cPanel)。Cookie包含一个以逗号分隔的会话名称组件。GET /,附带Authorization: Basic头,其解码值为<user>:\xff\nexpired=1。尾部的\nexpired=1是会话注入负载。来自步骤1的会话Cookie原样重放。Location头,提取cpsessXXXX令牌。GET /<cpsessXXXX>/,附带原始Cookie,并在响应体中查找msg_code:[expired_session]。其存在证明会话注入成功,主机易受攻击。在WHM(端口2087和443上的/___proxy_subdomain_whm路径)上,用户名是一个随机的u后跟十个十六进制字符。在cPanel(端口2083和443上的/___proxy_subdomain_cpanel路径)上,扫描器遍历其用户名单词表,并在第一个匹配处停止。
默认情况下,扫描器按顺序探测2087、2083和443,一旦任何界面确认漏洞即停止。
--exploit)CVE-2026-29205 — cPanel/WHM WP2 安全更新,2026年5月13日。在cPanel 11.134.0.26中修复。该公告跟踪的正是此利用链滥用的同一个
cpdavd权限降级回归问题。
cpdavd在端口2079(明文HTTP)和2080(TLS)上信任它构建的<principal>/<collection>/...路径,用于服务CalDAV/CardDAV资源。通过构造一个其路径组件编码了..片段的请求,并将其指向一个磁盘名称也编码了遍历(x-attachment-1-y)的maildir文件夹,可以迫使cpdavd以root身份读取磁盘上的任意文件,无论所有权或权限如何——包括/etc/shadow、/etc/passwd以及每个用户的邮件存储。
本应读取前将权限降级到账户所有者的纵深防御默认失效了:Cpanel::AccessIds::ReducedPrivileges对象在void上下文中构造,因此其析构函数在读取运行之前就恢复了root权限。cPanel 11.134.0.26将该对象绑定到一个my $privs词法变量上,使其在-f / stat / open / read链期间存活;因此在已修补的主机上,读取以非特权账户所有者身份运行,而不是root。
易受攻击的文件夹必须在读取之前存在于磁盘上。cPanel会在邮件首次到达子地址<user>+x-attachment-1-y@<domain>时自动为收件人创建一个名为.x-attachment-1-y的文件夹。因此,该链是:
info、admin、webmaster)。<前缀>+x-attachment-1-y@<domain>。跟踪接受的RCPT TO响应。/calendar/和/addressbook/集合前缀下,向cpdavd的端口2080(TLS)和2079(明文)发送路径遍历GET请求。成功时返回文件的字节;发现记录将记录使用的电子邮件、集合、字节数以及前200个字节作为预览。
此检查默认禁用,除非传递--exploit。启用时需要一个可用的出站SMTP中继(请参阅下面的配置文件),因为文件夹创建步骤无法跳过。
该利用仅对cPanel的电子邮件帐户功能下配置的真实虚拟电子邮件账户有效。通配地址不起作用——通配通过Exim的system_aliases路由器路由,永远不会到达dovecot_virtual_delivery传输,因此永远不会触发产生.x-attachment-1-y/文件夹的lda_mailbox_autocreate路径。
当您已经知道目标上有效的虚拟电子邮件时,请使用--email传递:
python scanner.py --config scanner.ini --email [email protected] target.com
--email完全跳过证书SAN枚举和前缀单词表,仅向您提供的地址发送一封邮件。可以重复使用以针对多个已知账户。定向路径比喷洒路径可靠得多。
如果没有--email,扫描器回退到从主机的TLS证书中提取的每个域名喷洒约15个常见前缀(info、admin、webmaster等)。Assetnote在200个暴露cpdavd的主机样本上的测量结果是约10%的喷洒命中率:喷洒模式的干净结果很弱地表明主机已修补,而使用真实账户的--email重新运行是唯一可靠的确认方式。
利用模式通过--config读取一个INI文件:
python scanner.py --config scanner.ini --exploit example.com
将scanner.ini.example复制到scanner.ini,填入SMTP凭据,并可选择调整CalDAV默认值。scanner.ini在.gitignore中,因此填充后的副本保留在本地。SMTP密码也可以通过SCANNER_SMTP_PASSWORD环境变量提供,该变量仅在文件中password字段为空时优先。
pip install -r requirements.txt
需要Python 3.8或更高版本。
单个目标:
python scanner.py example.com
多个目标通过位置参数:
python scanner.py host-a.example.com host-b.example.com:2083
一个目标文件,每行一个目标。以#开头的行被忽略:
python scanner.py -f targets.txt
从标准输入读取目标:
cat targets.txt | python scanner.py
目标可以是主机名或host:port。当指定端口时,扫描器仅探测该端口;否则探测2087、2083和443。
-u, --users — 在cPanel界面上尝试的cPanel用户名,以逗号分隔。默认为一个小的内置列表。-U, --users-file — 每行一个cPanel用户名的文件。-p, --ports — 当目标未指定端口时探测的端口,以逗号分隔。默认为2087,2083,443。-t, --threads — 每个目标用于遍历cPanel界面用户名列表的线程数。默认为10。-c, --concurrency — 并行扫描的目标数。默认为20。-T, --timeout — 每个请求的超时时间(秒)。默认为15。-o, --output — 将发现的漏洞目标逐行追加到此文件。--json — 每个目标向此文件写入一条JSON Lines记录。-q, --quiet — 仅在stdout上打印漏洞目标。连接失败和干净的目标仍记录在--json中并在摘要中计数。--no-progress — 禁用进度条。--exploit — 启用CalDAV路径遍历链。默认禁用;请参阅了解此标志解锁的副作用。每个发现向stdout输出一行,因此同时运行两个检查的目标将打印两次:
[!] 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
--json输出每个目标一条记录,包含一个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是所有发现中最差的情况。
最后会向stderr写入一个包含总数的摘要行。进度条渲染在stderr上,当stderr不是终端时自动抑制。
退出码:如果任何目标易受攻击则为0,如果每个可到达的目标都是干净的则为1,如果无法到达任何目标则为2。
扫描目标列表,将命中结果写入文件,并在stdout上保持静默:
python scanner.py -f targets.txt -o vulnerable.txt -q
使用自定义用户名列表扫描cPanel界面,增加并行度,输出JSON以便下游处理:
python scanner.py -f targets.txt -U cpanel-users.txt -c 100 --json results.jsonl
探测非默认端口集:
python scanner.py -p 2083,2087,8443 -f targets.txt
针对单个目标端到端运行CalDAV链,使用邮件喷洒回退(需要填充好的scanner.ini):
python scanner.py --config scanner.ini --exploit example.com
针对已知虚拟电子邮件的定向利用——命中率远高于喷洒:
python scanner.py --config scanner.ini --email [email protected] example.com
仅针对已确认的cpdavd主机列表运行CalDAV链,将/etc/passwd预览转储到JSON:
python scanner.py --config scanner.ini --caldav-only \
-f cpdavd-hosts.txt --json caldav-results.jsonl
默认的scanner.py <target>调用仅运行CVE-2026-41940检测器。它发送确认会话注入所需的请求,仅此而已。它不会以任何真实用户身份登录,不会针对root账户,不会升级到shell,也不会在目标系统上为任何有效账户累积失败密码事件。它匹配的标记(msg_code:[expired_session])是应用程序自身响应注入的expired=1会话字段时生成的,与cPanel登录页面在重放过期会话时使用的指标相同。
传递--exploit(或--caldav-only)会解锁CalDAV路径遍历链。这不再是一个检测器——而是一个有效的利用。启用后,扫描器将为每个域名枚举成功的目标执行以下操作:
scanner.ini中配置的中继打开一个真实的SMTP会话,并为每个候选收件人发送一封短消息(通常每个域名10-15封,每个目标3个域名);--read-file文件。默认文件是/etc/shadow。成功读取返回文件字节;空主体表示目标不可达,或者主机已应用权限降级修复(cPanel 11.134.0.26,my $privs = …)。检测比较的是接收到的主体长度是否为零,而不是响应的Content-Length(该值来自攻击者选择路径的stat(),即使后续的open()被拒绝,它也会被填充)。
为了独立于权限降级修复测试遍历可达性——例如,当您想知道cpdavd是否可达以及maildir前提是否在已修补的主机上满足时——请使用--read-file /etc/passwd重新运行。/etc/passwd是全局可读的,因此它在补丁前和补丁后的主机上都返回字节;结合两个信号(/etc/shadow主体存在=补丁前的root读取;/etc/shadow为空但/etc/passwd存在=遍历可达但已应用权限降级修复)可以明确地对主机进行分类。
内容和200字节预览会写入JSONL输出并在stdout上打印。每个域名的CalDAV利用可能需要一分钟或更长时间——这是等待邮件投递的重试阶梯,而不是挂起。
仅针对您拥有或已获得明确书面授权测试的资产运行--exploit。SMTP流量会被配置的中继和每个收件人邮件系统记录;文件读取会被cpdavd记录。
--config — 包含SMTP中继凭据和CalDAV可调参数的INI文件。请参阅scanner.ini.example。--read-file — CalDAV链成功时窃取的文件。覆盖配置文件中的值。默认为/etc/shadow——一个仅root可读的文件,因此成功读取可区分补丁前(返回shadow内容)和补丁后(open被拒绝,主体为空→报告不易受攻击)。使用--read-file /etc/passwd来测试遍历可达性,而不区分是否已修补。--caldav-only — 跳过41940检查,仅运行CalDAV链。隐含--exploit。对于针对已知可到达CalDAV的目标列表重新运行链非常有用。--email ADDR — 目标上的已知虚拟电子邮件账户。跳过证书SAN枚举和喷洒单词表;仅向ADDR发送一封邮件并针对该主体进行读取。可以重复使用。隐含--exploit。请参阅定向利用 vs 喷洒利用。-v, --verbose — 输出CalDAV链的每个域名的进度(证书SAN列表、喷洒数量、重试阶梯)。