文章总结: CVE-2026-41940是cPanel/WHM中一个高危认证绕过漏洞,攻击者通过CRLF注入伪造session字段绕过密码与双因素认证,直接获取ROOT权限并实现RCE。漏洞成因是set_pass函数未过滤回车符,且saveSession未调用CRLF过滤函数。影响全球超7000万设备,建议及时更新补丁。 综合评分: 87 文章分类: 漏洞分析,WEB安全,渗透测试,红队,内网渗透
CVE-2026-41940 深度分析影响7000万+量级的cPanel/WHM认证绕过漏洞:从CRLF注入到匿名RCE
原创
KCyber KCyber
自在安全
2026年5月5日 07:01 北京
在小说阅读器读本章
去阅读
欢迎大家关注自在安全公众号。为更好学习交流,建了个技术交流群,大家可以扫描进群。你也可以关注公众号后@我拉你进群。
cPanel/WHM 是一套基于 Linux 的虚拟主机管理面板,提供网站和服务器管理功能,号称全球装机数量超过 7000 万。CVE-2026-41940 是一个影响 cPanel/WHM 的严重认证绕过漏洞,该漏洞可以直接绕过所有密码和双因素认证,获取系统 ROOT 权限。
进程与认证
2087 端口对应 cPanel/WHM 后端管理服务,对应进程名为 cpsrvd-dormant ,这是一个 Perl 解释器嵌入的程序:
继续分析发现 cpsrvd-dormant 只是一个 launcher,实际处理逻辑位于 cpsrvd 之中。在服务器的 perl 脚本中并没有找到核心认证函数,逆向分析 cpsrvd 发现多个认证相关函数,比如 handle_auth 、 handle_one_connection 等,这些函数均编译在二进制内部,使用静态反编译无法获取处理源码。为了获取实际处理代码,可以采用 GDB attach 到运行中的 cpsrvd 子进程,在 Perl 解释器运行时完成源码还原。这个过程让 AI 来辅助分析效率很高:
获取源码后,我们才能真正完整弄明白整个认证过程。
form 表单认证
form 表单认证数据包如下:
POST /login/?login_only=1 HTTP/1.1
Host: 127.0.0.1:2087
user=test&pass=test&goto_uri=%2F
处理函数为 handle_form_login ,首先检查请求路径是否为登录 URL :
my $document = $server_obj->request->get_document;
if ($document =~ //u) { # 匹配 /login
# todo
}
进入 form login 分支,解析 POST 请求 body :
my $login_form_ref = $server_obj->timed_parseform(60);
调用 Cpanel::Form::parseform 解析表单数据。密码验证失败会调用 badpass 函数,在返回 401 之前仍然会创建 session 并返回对应的 Cookie 值 whostmgrsession :
sub badpass {
my (%OPTS) = @_;
...
note_cphulk_failure(...) if $OPTS{'notefailure'};
if (!$session && !$OPTS{'skip_session_creation'}) { # [1]
my $new_session = Cpanel::Session::create(
'user' => '',
'pass' => '',
'needs_auth' => 1,
'ip_address' => $ENV{'REMOTE_ADDR'},
);
# 通过 Set-Cookie 返回给客户端
# Cookie 值包含 ,obhex 后缀
}
# 返回 401 响应
send_401_response(...);
}
create 函数代码没有编译到二进制文件中,而是位于 /usr/local/cpanel/Cpanel/Session.pm :
sub create {
my (%args) = @_;
my $session_name = _generate_session_name();
my $ob = _generate_ob_secret();
filter_sessiondata(\%args); # 过滤
write_session($session_name, \%args);
return "$session_name,$ob";
}
函数内会调用 filter_sessiondata 过滤 CRLF 。此时 write_session 写入文件到 /var/cpanel/sessions/raw 目录,文件内容如下:
Basic Auth 认证
cPanel/WHM 还支持 AUTH_HEADER 认证。如果携带 Cookie 访问非 /login 接口,将进入 handle_auth 函数完成 session 加载:
sub handle_auth {
my $session = $server_obj->get_current_session;
my $SESSION_ref;
if ($session and ($session ne 'closed')) { # session
$session =~ tr[/][]d; # 删除 / ,防止路径穿越
$SESSION_ref = Cpanel::Session::Load::loadSession($session);
进入 loadSession ,将移除 cookie 中的 ob 后缀,后续会加载前面 badpass 调用 create 创建的文件,最终获取 session_ref 对象:
sub loadSession {
my ($session) = @_; # session
my $ob = get_ob_part(\$session);
my $cache_file = "/var/cpanel/sessions/cache/${session}.json";
if (-f $cache_file) {
my $json_data = Cpanel::AdminBin::Serializer::Load($cache_file);
if ($json_data && ref $json_data eq 'HASH') {
if ($ob && $json_data->{'pass'}) {
my $encoder = Cpanel::Session::Encoder->new('secret' => $ob);
$json_data->{'pass'} = $encoder->decode_data($json_data->{'pass'});
}
return $json_data;
}
}
my $raw_file = "/var/cpanel/sessions/raw/${session}";
...
# 加载解析
return $session_ref;
}
回到 handle_auth 的 Basic Auth 解析环节,获取 header 中的 Authorization ,进行 base64 解码提取 user 和 pass :
my $auth_header = $server_obj->request->get_headers->{'authorization'};
if (not($auth_header)) {
...
}
else {
my($authtype, $encoded) = split(/ /u, $auth_header, 2);
if ($authtype =~ /^Basic$/iu) {
$server_obj->auth->set_auth_type('basic');
my($user, $pass) = split(/:/u, decode_base64($encoded), 2);
$user = $server_obj->auth->set_user($user);
$pass = $server_obj->auth->set_pass($pass);
漏洞触发的关键点位于 set_pass 函数,该函数实现代码如下,没有对 \r\n 字符进行过滤检查:
# Cpanel::Server::Auth
sub set_user {
my ($self, $user) = @_;
$user =~ s{[\0/]}{}g; # [1]
$self->{'user'} = $user;
return $user;
}
sub set_pass {
my ($self, $pass) = @_;
$pass =~ s/\0//g; # [2]
$self->{'pass'} = $pass;
return $pass;
}
随后调用 _validate_username 验证用户名是否合法(root 合法)。在密码验证之前,请求数据会先保存到 session 中,在 handle_auth 返回前,这个数据写入操作已经完成。这里与前面 session create 的区别在于,没有调用 filter_sessiondata 进行 CRLF 过滤:
sub saveSession {
my ($session, $session_ref, %options) = @_;
my $ob = get_ob_part(\$session);
return 0 if !is_valid_session_name($session);
my $encoder = $ob && Cpanel::Session::Encoder->new('secret' => $ob);
local $session_ref->{'pass'} = $encoder->encode_data($session_ref->{'pass'})
if $encoder && length $session_ref->{'pass'};
write_session($session, $session_ref);
}
CRLF 注入
注入的 \r\n 在 raw 文件中会被解析为独立的行。这是因为 Cpanel::Config::LoadConfig::loadConfig 在读取 session raw 文件时,会检测文件内容是否包含 \r ,如果包含则使用 \r?\n 作为行分隔符进行 split :
my $has_cr = ($cfg_txt =~ /\r/);
my $split_on = $has_cr ? '\r?\n' : '\n';
$conf_ref = {
map { ($k, $v) = (split(m/=/, $_, 2))[0, 1]; defined($v) ? ($k, $v) : () }
split(/$split_on/, $cfg_txt)
};
构造测试请求发现虽然返回 401 ,但是 CRLF 注入实际已经完成:
认证绕过到 RCE
上面的 CRLF 注入能用来干什么呢?handle_auth 函数返回后,后续在调用 docheckpass_whostmgrd 时,会通过 check_authok_user 函数进行用户验证,当存在 session_ref 对象中存在 successful_internal_auth_with_timestamp 键值,并且获取的时间戳在合理范围之内,会直接返回 AUTH_OK ,完全跳过后续的密码验证逻辑:
sub check_authok_user {
my (%OPTS) = @_;
my $user = $OPTS{'user'};
my $session_ref = $server_obj->get_session_ref;
if ($session_ref->{'successful_internal_auth_with_timestamp'}) { # [1]
my $timestamp = $session_ref->{'successful_internal_auth_with_timestamp'};
if ($timestamp > (time() - $SESSION_TIMEOUT)) {
return $AUTH_OK;
}
}
my $check_result = Cpanel::CheckPass::UNIX::checkpassword(
$user, $OPTS{'pass'}, $OPTS{'encrypted_pass'}
);
return $check_result ? $AUTH_OK : $AUTH_FAILED;
}
很容易构造注入 successful_internal_auth_with_timestamp 的请求绕过认证并获取 ROOT 权限。
后续可结合一些 API 接口实现 RCE ,具体过程从略。
CVE-2026-41940 是一个多步骤的认证绕过漏洞,最终可导致匿名 RCE。漏洞触发的关键是set_pass没有进行CRLF过滤,同时saveSession也没有像create函数那样调用filter_sessiondata进行过滤,导致产生了CRLF注入。此外,关键的perl代码其实被编译进了cpsrvd,审计前需要进行动态提取,这种有难度的操作现在用AI 来辅助很容易实现。
由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用本人负责,公众号及文章作者不为此承担任何责任。
欢迎大家关注自在安全公众号。为更好学习交流,建了个技术交流群,大家可以扫描进群。你也可以关注公众号后@我拉你进群。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:自在安全 KCyber KCyber《CVE-2026-41940 深度分析影响7000万+量级的cPanel/WHM认证绕过漏洞:从CRLF注入到匿名RCE》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论