GitHub环境变量泄露与GHES远程代码执行漏洞剖析

本文详细分析了CVE-2024-0200漏洞的发现过程,该漏洞允许攻击者通过Ruby反射机制泄露GitHub生产环境的所有环境变量,并在GitHub Enterprise Server上实现远程代码执行,影响了GitHub.com和启用GitHub Actions的企业服务器。

Send()-ing Myself Belated Christmas Gifts - GitHub.com’s Environment Variables & GHES Shell

Backstory

2023年12月初,我在研究GitHub Enterprise Server(GHES)时发现了一个潜在的小漏洞。直到圣诞节后,我才找到时间分析这个漏洞,当时我完全没想到它会有如此大的影响。

A Quick Primer on Ruby Reflections

与JavaScript类似,Ruby中几乎所有东西(如布尔值、字符串、整数)都是对象。Object包含了Kernel模块作为混入,使得Kernel模块中的方法可以被每个Ruby对象访问。值得注意的是,可以使用Kernel#send()进行反射(即间接方法调用):

1
2
3
4
5
6
7
8
9
class HelloWorld
  def print(*args)
    puts("Hello " + args.join(' '))
  end
end

obj = HelloWorld.new()
obj.print('world') # => 'Hello World'
obj.send('print', 'world') # => 'Hello World'

如上所示,可以使用Kernel#send()动态调用方法,对任何对象执行反射。

Discovering the Vulnerability

在代码库中快速搜索后,我在Organizations::Settings::RepositoryItemsComponent(位于app/components/organizations/settings/repository_items_component.rb)中发现了一个未经验证的Kernel#send()调用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Organizations::Settings::RepositoryItemsComponent < ApplicationComponent
  def initialize(organization:, repositories:, selected_repositories:, current_page:, total_count:, data_url:, aria_id_prefix:, repository_identifier_key: :global_relay_id, form_id: nil)
    @organization = organization
    @repositories = repositories
    @selected_repositories = selected_repositories
    @show_next_page = current_page * Orgs::RepositoryItemsHelper::PER_PAGE < total_count
    @data_url = data_url
    @current_page = current_page
    @aria_id_prefix = aria_id_prefix
    @repository_identifier_key = repository_identifier_key # [2]
    @form_id = form_id
  end
  
  def identifier_for(repository)
    repository.send(@repository_identifier_key) # [1]
  end
end

在[1]处,repository.send(@repository_identifier_key)在identifier_for()方法中被调用,没有对@repository_identifier_key(在[2]处设置)进行任何先前的输入验证。这允许调用对象可访问的所有方法(包括私有或受保护的方法,以及从祖先类继承的任何其他方法)。

In Search of Impact

这不是仅限于从Object继承的方法——我们可以通过查看Repository对象可访问的方法来扩展候选方法的搜索。

关于"零参数任意方法调用"的假设:我们实际上可以调用需要参数的方法——只要它们有默认值!

Getting the Environment Variables

在Repository::GitDependency模块(位于packages/repositories/app/models/repository/git_dependency.rb)中,包含了危险方法nw_fsck():

1
2
3
4
5
module Repository::GitDependency
  def nw_fsck(trust_synced: false)
    rpc.nw_fsck(trust_synced: trust_synced)
  end
end

这个nw_fsck()方法非常不起眼,但包含了丰富的信息。GitRPC::Native.spawn()返回一个包含传递给git进程的环境变量的Hash对象,而Repository::GitDependency#nw_fsck()将这个Hash返回给我们,使我们能够泄露传递给Rails的所有环境变量!

The Actual Impact

我无意中获得了总共1220个环境变量(约2MB),包含大量生产访问密钥。我立即停止测试,联系GitHub人员提醒他们这一事件,并在不久后提交了漏洞报告。

Getting Remote Code Execution (RCE)

环境变量列表包括ENTERPRISE_SESSION_SECRET,该密钥用于签名存储在会话cookie中的序列化数据。如先前在@iblue的GitHub Enterprise管理控制台远程代码执行中演示的那样,了解ENTERPRISE_SESSION_SECRET值允许攻击者签名任意序列化数据。

Ruby on Rails使用加密签名的序列化Ruby Hash实现会话存储。这个Hash使用Marshal.dump序列化为cookie,随后使用Marshal.load反序列化。如果攻击者可以构造有效签名,他们可以创建一个包含传递给Marshal.load的任意输入的会话cookie。根据Ruby文档对Marshal.load的说明,这可能导致代码执行。

Exploit Conditions

此漏洞影响GitHub.com和任何启用了GitHub Actions的GitHub Enterprise Server。攻击者还需要拥有组织所有者角色。

Suggested Mitigations

  • 在Orgs::ActionsSettings::RepositoryItemsController类中针对允许列表验证rid_key参数,确保只能调用Repository类的预期方法
  • 撤销并重新生成GitHub.com/任何可能已受损的GitHub Enterprise Server中使用的所有密钥
  • 考虑使用功能所需的最小环境变量集来生成git进程

Detection Guidance

可以通过检查服务器的访问日志来检测此漏洞的利用情况,查找设置了异常rid_key参数值的所有对/organizations/<organization_name>/settings/actions/repository_items的请求。

Timeline

  • 2023-12-26 供应商披露
  • 2023-12-26 初始供应商联系
  • 2023-12-26 在Github.com生产服务器上热修复
  • 2023-12-28 向供应商发送RCE报告
  • 2024-01-16 供应商补丁和公告发布
  • 2024-05-06 公开发布

Closing Thoughts

遗憾的是,这个漏洞在一个非常不合时宜的时间(圣诞节后)被发现和利用。我要向所有在圣诞/新年节日期间工作的Hubbers表示诚挚的歉意和感激,因为这个错误报告为他们创建的工作量一定非常巨大。然而,看到他们快速修补这个漏洞,轮换所有密钥(一个非常痛苦的过程),以及运行并得出结论确认此漏洞先前未在野外被利用,令人印象深刻。

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