关键信息
Sonar 的漏洞研究团队发现了一个导致流行的内容管理系统Joomla中存在多个 XSS 漏洞的问题。
在SonarCloud的帮助下发现的问题影响 Joomla 的核心过滤器组件,并被跟踪为CVE-2024-21726。
攻击者可以利用该问题诱骗管理员单击恶意链接来获得远程代码执行。
潜在的 PHP 错误是 PHP 的 mbstring 函数处理无效多字节序列的方式不一致。
该错误已在 PHP 8.3 和 8.4 版本中修复,但未向后移植到较旧的 PHP 版本。
Joomla 发布了安全公告并发布了5.0.3/4.4.3 版本,缓解了该漏洞。
Joomla
Joomla 是一个免费的开源内容管理系统 (CMS),用于构建网站和在线应用程序。大约2% 的网站使用 Joomla,这使其成为最受欢迎的 CMS 之一,在全球拥有数百万次部署。
Joomla 的广泛使用以及大多数部署均可公开访问的事实使其成为威胁行为者的宝贵目标。就在最近,Joomla通过不当访问控制漏洞 (CVE-2023-23752)成为针对不同组织的攻击目标。
在本文中,我们深入研究了SonarCloud检测到的一个有趣的 XSS 问题,它使我们陷入了兔子洞,发现了 PHP 中的一个错误。我们将解释攻击者如何利用 PHP mbstring 函数的不一致来绕过 Joomla 的输入清理,从而引入多个 XSS 漏洞。
影响
Joomla 5.0.2/4.4.2 及以下版本容易出现多个 XSS 漏洞。攻击者诱骗管理员点击恶意链接可以获得远程代码执行(RCE):
无论 PHP 版本如何,Joomla版本 5.0.3/4.4.3 都可以缓解该问题。 PHP 版本 8.3 和 8.4 修复了底层 PHP 错误,但未向后移植到较旧的 PHP 版本。
我们强烈建议将 Joomla 更新到最新版本并保持您的 PHP 版本为最新。
技术细节
在我们不断努力帮助保护开源项目并改进我们的清洁代码解决方案的过程中,我们定期通过SonarCloud扫描开源项目并评估结果。在扫描 Joomla 时,SonarCloud 报告了一个有趣的 XSS 问题:
这个小代码片段取自管理面板上的设置页面。根据提出的问题,查询参数forcedItemType反映在输出中,这引入了 XSS 漏洞。
请注意,用于检索查询参数的方法的第三个参数get设置为string。该值确定应将哪些过滤器应用于查询参数。在幕后,该get方法使用该类JoomlaFilterInputFilter来清理潜在的恶意输入,这应该可以防止 XSS 攻击。
过滤器逻辑相当复杂,并使用一个名为 的方法cleanTags来删除所有未明确允许的 HTML 标签。对于查询参数,根本不允许使用任何标签。
因此,对于以下示例输入:
some-text<script>alert(1)</script>
...,<script>标签被删除,结果是这样的输出:
some-textalert(1)
该cleanTags方法通过确定任何开始标记 ( ) 的位置<,然后删除直到并包括相应结束标记 ( >) 之前的所有数据来执行此清理:
通过确定开始标记 ( )的偏移量来提取开始标记之前的字符(例如,some-text在上面的示例中),然后使用来提取它:$tagOpenStartStringHelper::strposStringHelper::substr
// Is there a tag? If so it will certainly start with a '<'.
$tagOpenStart = StringHelper::strpos($source, '<');
while ($tagOpenStart !== false) {
// Get some information about the tag we are processing
$preTag .= StringHelper::substr($postTag, 0, $tagOpenStart);
对于示例some-text<script>alert(1)</script>,第一次调用StringHelper::substr返回 string some-text,该字符串被附加到$preTag变量中:
alert(1)在第二次迭代时,添加字符串:
$preTag用于收集所有已清理子字符串的变量稍后将作为最终结果返回:
// ...
return $preTag;
}
和方法只是各自 PHP mbstring函数StringHelper::strpos和的包装器。StringHelper::substr mb_strpos
mb_substr
在确定这种清理是否安全时,我们注意到 PHP 函数mb_strpos,和mb_substr处理无效的 UTF-8 序列的方式不同。当mb_strpos遇到UTF-8 前导字节时,它会尝试解析以下连续字节,直到读取完整的字节序列。如果遇到无效字节,则之前读取的所有字节都被视为一个字符,并在无效字节处重新开始解析:
因此,以下调用mb_strpos返回索引4:
mb_strpos("xf0x9fAAA<BB", '<'); // 4
<该索引是左尖括号( 3c) 字符在字符串中的位置。
mb_substr另一方面,当遇到前导字节时,会跳过连续字节:
这意味着mb_substr,前四个字节被视为一个字符,并且左尖括号<( 3c) 字符具有索引2。因此,当使用返回的索引时,以下对mb_substr returns的调用"xf0x9fAAA<B"
mb_strpos
mb_substr("xf0x9fAAA<BB", 0, 4); // "xf0x9fAAA<B"
由于两个函数之间存在这种不一致,Joomla 的清理功能在遇到此无效 UTF-8 字节序列时不仅会提取左尖括号之前的文本,还会提取左尖括号本身和以下字符:
攻击者可以插入多个无效的 UTF-8 序列,这会有效地将返回的索引偏移到StringHelper::strpos左尖括号之外,从而在清理后的输出中包含任意 HTML 标签。这完全绕过了 Joomla 所应用的清理。由于此问题影响 Joomla 的核心过滤器功能(该功能在整个代码库中使用),因此会导致多个 XSS 漏洞。
例如,攻击者可以利用由此产生的 XSS 漏洞之一来制作恶意链接。当管理员单击此链接时,注入的 JavaScript 有效负载可用于自定义模板并插入任意 PHP 代码。因此,攻击者可以通过欺骗管理员单击恶意链接来获得远程代码执行 (RCE)。
修补
Joomla 通过用 PHP 的常规字符串函数替换 mbstring 函数的使用来解决这个问题:
// Is there a tag? If so it will certainly start with a '<'.
- $tagOpenStart = StringHelper::strpos($source, '<');
+ $tagOpenStart = strpos($source, '<');
while ($tagOpenStart !== false) {
// Get some information about the tag we are processing
- $preTag .= StringHelper::substr($postTag, 0, $tagOpenStart);
+ $preTag .= substr($postTag, 0, $tagOpenStart);
这些函数之间的区别在于 PHP 的常规字符串函数不支持多字节,而是对单字节进行操作。由于所应用的清理不需要多字节感知,因此应该首选这些函数。
我们还向 PHP 维护人员报告了 mbstring 函数的不一致行为,因为我们认为这是无意的。 PHP 维护者提供了一个补丁,该补丁通过在遇到前导字节时不跳过连续字节来使行为保持一致。不幸的是,该问题未被归类为与安全相关的问题,这意味着该补丁不会向后移植到旧版本的 PHP。
有关 PHP mbstring 函数和补丁的行为的更多背景信息可以在相关提交消息中 Alex Dowad 的精彩解释中找到。
时间线
日期行动
-
2023-11-22我们向 Joomla! 报告该漏洞。安全突击队
-
2023-11-28Joomla!安全打击小组证实了我们的发现。
-
2023-12-01我们向 PHP 维护人员报告不一致的 mbstring 函数行为
-
。
-
2023-12-10PHP 维护者提供了一个补丁,适用于PHP 8.3 和 8.4。
-
2024-02-20Joomla 发布了 5.0.3/4.4.3 版本,无论 PHP 版本如何,该版本可以缓解该问题。
-
2024-02-20Joomla 和 Sonar 协调发布安全公告。
-
2024-02-23添加了完整的技术细节。
原文始发于微信公众号(Ots安全):Joomla:PHP 错误引入多个 XSS 漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论