Dell UnityVSA预认证命令注入漏洞分析(CVE-2025-36604)

本文深入分析了Dell UnityVSA存储设备中发现的预认证命令注入漏洞CVE-2025-36604,详细介绍了漏洞触发路径、代码分析过程以及利用方法,涉及Perl模块安全审计和Apache配置分析等技术细节。

事情从不简单直到它变得简单(Dell UnityVSA预认证命令注入CVE-2025-36604)

欢迎回来,这一周真是精彩!我们为你经历的一切感到高兴和/或遗憾。事情会变得更好和/或更糟,但你很可能能够挺过去。

今天,我们将沿着花园小径漫步,深入档案库,发布我们对2025年3月在Dell UnityVSA解决方案中发现并披露给戴尔的一个漏洞的分析。

作为我们持续增强watchTowr平台中先发制人暴露管理技术的一部分,我们在利用watchTowr平台的组织的攻击面中看到的技术上执行零日漏洞研究。这为我们的客户提供了主动防御,并在我们与供应商和项目联系以寻求适当修复的同时,提供了漏洞的前瞻性可见性。

3月,我们报告了Dell UnityVSA版本5.5.0.0.5.259(我们假设早期版本也存在)中的一个预认证命令注入漏洞。一如既往,我们更容易像机器人一样引用这个漏洞,使用充满爱意分配的CVE-2025-36604。

Dell UnityVSA(虚拟存储设备)是Dell Unity存储平台的软件定义版本。UnityVSA不是运行在专用的Dell存储硬件上,而是作为虚拟机在VMware ESXi等管理程序上运行,为您提供大部分Unity功能的纯软件包。

众所周知,存储及围绕存储生态系统的解决方案对互联网居民具有实质性意义,主要且明显的原因是它们提供了通往敏感数据的清晰路径,这些数据要么1.可用,要么2.可被勒索软件加密。

带着这个令人振奋和激励的事实,让我们开始吧…

UnityVSA、戴尔和14个CVE

为了铺垫背景,让我们看看戴尔发布补丁后UnityVSA的情况 - 迅速被戴尔Unity安全发布说明打脸(可能是友好的):

有人设法找到了14个(是的,十四个!)预认证命令注入漏洞 - 我们可以通过两种方式看待这些信息:

  • 垃圾场大火,或者,
  • 现在这是一个清理过的、编写良好且安全的代码库。

我们不得不弄清楚 - 神秘感和潜在的惊喜太诱人了 - 于是我们很快地对修补后的漏洞进行了差异比较。

我们使用了:

  • Dell UnityVSA 5.4.0.0.5.094(已知存在漏洞)
  • Dell UnityVSA 5.5.0.0.5.259(假设通过14个全面补丁后完美)

AccessTool.pm

在差异结果中,有一个文件特别引起了我们的注意。AccessTool.pm站在那里 - 嘲笑着我们,并可能扮演着我们补丁差异分析焦点的罪魁祸首:

快速浏览Perl模块显示了多个修复 - 包括开发人员以注释形式给自己的有用提醒,关于一些讨厌的不需要的命令注入问题。

当然,这本身并不坏,我们讨论过"代码异味"的概念 - 这是我们大脑转化为直觉的指标。让我们看看。

该代码片段位于一个名为getCASURL的Perl函数内部。

差异显示,对于有灵感的人来说,这看起来像是一个教科书式的Perl命令注入 - 攻击者影响的值(如$host)被拼接成一个单一的$exec_cmd字符串。在修补版本中,相同的输入被包裹在单引号中,以尝试消除shell元字符。

最后的致命一击是$exec_cmd使用经典的Perl反引号运算符运行,因此任何转义失误都是您远程命令执行所需的全部。

getCASURL

此时,我们问自己:在这个显然强大的代码库中是否还有更多?

再看一下上面的截图。你看到我们看到的了吗?让我们放大。

事实证明,如果$type设置为字面值"login",那么在574行,$uri直接连接到我们的$exec_cmd - 最终命令中。

明显的问题:为什么这个变量没有被转义?在其他任何地方,开发人员都花时间转义输入,但这里没有。他们是否假设它不受攻击者控制?或者我们正在观察未来的工作保障?

无论哪种方式……唉。

为了理解我们如何到达上面的代码片段,重要的是要注意代码驻留在一个名为getCASURL的函数中:

要到达$uri被连接的代码部分,传递给getCASURL的最终参数 - $type - 必须等于字面值"login"。这是控制连接逻辑的唯一条件;如果不满足,所讨论的代码路径将永远不会执行。

考虑到这一点,下一步是审查getCASURL的每个调用者,以识别$type被设置为"login"(或可以被影响成为"login")的地方。映射这些调用站点让我们将理论路径与实际可到达的路径分开,并将我们的分析集中在重要的场景上。

跟踪调用链

有了这个门控条件,明显的下一步是追踪谁实际调用了getCASURL:

这个函数有两个调用站点。如果执行到达getCASLoginURL,最终参数被设置为字面值"login",这满足了命中$uri连接所需的条件。

下一步是继续搜索并识别getCASLoginURL的每个调用者。映射这些调用站点将显示此路径在实践中的执行时间,并帮助我们评估实际的可达性。

进一步跟踪,我们发现调用源自make_return_address() - 这就是它变得有趣的地方:

进入getCASLoginURL的路径起源于make_return_address()。该助手通过标准的Perl调用$r->uri()检索入站请求URI,然后使用该值调用getCASLoginURL。

当我们看到AccessHandler.pm如何使用它时,情节变得更加复杂。

继续跟踪,make_return_address()从AccessHandler.pm被调用。该模块中的handler()函数在第153行检查请求是否缺少cookie。如果不存在cookie,用户被视为未认证,并被重定向到登录页面,这会触发使用当前HTTP上下文调用make_return_address($r)。

Apache配置

当然,除非处理程序实际执行,否则这一切都不重要。答案位于Apache的配置中:

正如我们所见,Apache的配置为相关请求范围注册了该函数。

在httpd.conf中,我们观察到一个配置条目将匹配的请求路由到AccessHandler::handler,确保在处理请求期间调用该模块。

实际上,这意味着handler()为落入该范围的每个传入请求运行。当请求到达时没有预期的cookie,代码通过调用make_return_address($r)触发重定向到登录流程,这反过来导致getCASLoginURL(…)。该路径仅在$type等于"login"时到达$uri连接。

这实际上意味着开发人员通过使用PerlModule指令加载Perl模块,注册了一个要在每个请求上执行的回调。因此,AccessHandler::handler为Web服务器处理的每个请求调用。

流程映射

这是几个移动部分 - 让我们退后一步映射流程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[HTTP Request]
        |
        v
+-------------------------------------------+
| Apache httpd.conf                         |
| PerlModule MOD_SEC_EMC::AccessHandler     |
+-------------------------------------------+
        |
        v
+----------------------------------------+
| MOD_SEC_EMC::AccessHandler::handler($r)|
+----------------------------------------+
        |
        v
+----------------------------------------+
| AccessTool::make_return_address($r)    |
|  - derives $uri_to_use_raw  <----------|--[RAW用户控制的URI]
+----------------------------------------+
        |
        v
+----------------------------------------+
| getCASLoginURL($r, $uri_to_use_raw)    |
|  - 转发原始URI                         |
+----------------------------------------+
        |
        v
+--------------------------------------------------+
| getCASURL($r, $uri=$uri_to_use_raw, $scheme,     |
|           $type="login")                         |
|           ...                                    |
|           ...                                    |
|           ...                                    |
|  if ($type eq "login") {                         |
|     $exec_cmd .= $uri;        # 连接原始URI      |
|     my $output = `$exec_cmd`; # shell执行        |
|  }                                               |
+--------------------------------------------------+

完美。这意味着我们可以通过向任何在请求URI中包含有效命令注入负载的页面发出HTTP请求来执行登录重定向路径。我们必须确保请求不携带认证cookie,以便触发处理程序。

事情从不简单直到它变得简单

链清晰了,下一个问题很明显:当我们尝试时会发生什么?

起初,看起来我们有一个明确的胜利。但现实有其他计划(一如既往):

我们的第一次尝试失败了:因为URI没有指向有效资源,Perl模块从未触发。

这很好地解释了为什么这个路径可能长时间未被发现 - 没有解析,没有处理程序,没有漏洞。

但是,如果我们给它一个确实需要解析的URI呢?在这种情况下,处理程序开始行动,突然我们一直在追逐的代码路径可能变得活跃。

检测工件生成器

按照惯例,我们分享此漏洞的检测工件生成器 - 使团队能够识别其环境中的易受攻击主机。

时间线

我们要感谢Dell PSIRT在处理此安全报告时的专业精神和响应能力。

日期 详情
2025年3月28日 WT-2025-0037被发现并披露给戴尔。
2025年3月28日 watchTowr开始在客户端攻击面上寻找漏洞。
2025年3月31日 戴尔确认收到WT-2025-0037报告并打开票据VRT-29331。
2025年7月31日 戴尔PSIRT发布安全公告,随后发布产品新版本(5.5.1)。
2025年10月3日 博客文章发布。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计