RubyGems.org远程代码执行漏洞
发布日期:2017年10月7日
概述
RubyGems.org是一个非常流行的Ruby依赖托管服务。本文披露了该平台存在的一个远程代码执行漏洞,通过反序列化漏洞实现攻击。漏洞已快速修复,官方公告参见CVE-2017-0903。
背景
任何编写过Ruby应用程序的开发人员都可能与RubyGems.org交互过。当运行gem install rails
时,gem工具会从rubygems.org获取rails gem及其所有依赖项。RubyGems.org本身是一个Rails应用程序,具有明确的责任披露政策。
漏洞详情
Ruby gems实际上是tar归档文件,包含三个主要文件:
metadata.gz
:包含gem元信息的YAML文件data.tar.gz
:包含所有源代码的tar归档checksums.yaml.gz
:包含gem内容加密哈希的YAML文件
问题根源
解析不受信任的YAML是危险的,因为YAML允许编码任意对象,类似于Python的pickle。当上传gem到rubygems.org时,应用程序调用Gem::Package.new(body).spec
,其中使用了不安全的YAML.load
调用。
虽然rubygems.org的开发者在2013年就意识到了这个问题,并通过monkey-patching限制只能反序列化白名单类,2015年还切换到了Psych.safe_load
,但修补并不充分。
漏洞利用
在#read_checksums
方法中存在关键的漏洞点:
|
|
攻击者可以控制YAML.load
的输入。通过构造特定的YAML对象,利用#sort
方法调用触发漏洞利用链:
- 构造恶意的
ActiveSupport::Cache::Entry
对象 - 当
#value
方法被调用且@compressed
为true时,会调用Marshal.load
处理DEFLATE压缩的攻击者数据 - 反序列化的对象被构造为执行任意代码
利用链分析
完整的利用链涉及多个类的交互:
Gem::Package::TarReader
包含Enumerable
,调用#sort
会触发#each
Gem::Package::TarReader::Entry#read
调用MemoryStore#read
ActiveSupport::Cache::MemoryStore
从@data
哈希中获取恶意条目- 最终触发恶意载荷执行
结论与启示
这个漏洞提醒我们YAML非常强大,在某些场景下应该使用表达力较弱但更安全的交换格式(如JSON)。建议未来YAML.load
可以修改为接受可选的类白名单参数,使复杂对象的反序列化成为选择加入的行为。
当前的YAML.load
应该命名为YAML.unsafe_load
来明确其危险性,而不是依赖用户知道何时应该使用YAML.safe_load
。
致谢
非常感谢rubygems.org团队运行了一个响应迅速的漏洞赏金计划。