RubyGems.org远程代码执行漏洞深度解析

本文详细分析了2017年RubyGems.org平台存在的反序列化漏洞,攻击者可通过恶意构造的gem包实现远程代码执行。文章深入探讨了漏洞原理、利用链构造过程以及修复方案,为开发者提供了重要的安全启示。

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方法中存在关键的漏洞点:

1
2
3
4
5
@checksums = gem.seek 'checksums.yaml.gz' do |entry|
  Zlib::GzipReader.wrap entry do |gz_io|
    YAML.load gz_io.read # 漏洞点
  end
end

攻击者可以控制YAML.load的输入。通过构造特定的YAML对象,利用#sort方法调用触发漏洞利用链:

  1. 构造恶意的ActiveSupport::Cache::Entry对象
  2. #value方法被调用且@compressed为true时,会调用Marshal.load处理DEFLATE压缩的攻击者数据
  3. 反序列化的对象被构造为执行任意代码

利用链分析

完整的利用链涉及多个类的交互:

  • 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团队运行了一个响应迅速的漏洞赏金计划。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计