变量引用
这个其实不是语言特性漏洞,而是代码逻辑漏洞。只有在特定代码情况下才会产生
KaTeX parse error: Expected 'EOF', got '&' at position 3: x=&̲a使两个变量同时指向同一个内存地址
利用:
KaTeX parse error: Expected 'EOF', got '&' at position 10: jay17->b=&̲jay17->a;
详细参照** NSS [UUCTF 2022 新生赛]ez_unser、私教web57
C绕过
C代替O能绕过wakeup,但那样的话只能执行construct()函数或者destruct()函数,无法添加任何内容。就绕过了wakeup。
O:4:“User”:2:{s:3:“age”;i:20;s:4:“name”;s:4:“daye”;}
—>
C:4:“User”:2:{}
C绕过-进阶 //愚人杯3rd [easy_php]
__unserialize()魔术方法
条件:PHP 7.4.0+
如果类中同时定义了 __unserialize()
和 __wakeup()
两个魔术方法,则只有 __unserialize()
方法会生效,__wakeup()
方法会被忽略。
对象的属性数量不一致
CVE-2016-7124
版本:PHP5 < 5.6.25、PHP7 < 7.0.10
绕过方式:
1.当序列化字符串中属性值个数大于属性个数,就会导致反序列化异常,从而跳过__wakeup()。
例如:O:4:“User”:2:{s:3:“age”;i:20;s:4:“name”;s:4:“daye”;}中将变量个数2(s:3:“age”;表示第一个变量的名字,i:20;表示第一个变量的值,因此像这种对象,都是成对出现的,几对就有几个变量)修改为3即可
str_replace(‘:1:’, ‘:2:’,$a);
原理:
反序列化后由于属性值个数不匹配,被PHP当作垃圾回收。(本质是GC回收机制)
----------------------------------------------------------------------
进一步探索,如果换成类包类的情况,就是类属性还是一个类。
//正常payload
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:3:“end”;s:1:“1”;}
//内部类属性数量不一致,只触发外部类的__destruct()
O:1:“A”:2:{s:4:“info”;O:1:“B”:2:{s:3:“end”;N;}s:3:“end”;s:1:“1”;}
//外部类属性数量不一致,先外类__destruct()后内类__wakeup()
O:1:“A”:3:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:3:“end”;s:1:“1”;}
原理:
反序列化,它是先从里面里面开始反序列话,而不是最外面。通俗讲,就是类A里面的属性是类B,反序列化先反序列化类B再反序列化类A。
内部类属性数量不一致,直接把内部类当垃圾回收,所以不触发内部类__wakeup(),只触发外部类的__destruct()。
外部类属性数量不一致,外部类直接被当成垃圾回收,先触发了外部类__destruct(),而内部类正常,就正常触发内部类__wakeup()。
这听起来像fast-destruct,不是像,就是同一个东西,其实本质上都是PHP的GC回收机制罢了。
fast-destruct
本质上就是利用GC回收机制。
方法有两种,删除末尾的花括号、数组对象占用指针(改数字)
$a = new a();
a
r
r
y
=
a
r
r
a
y
(
arry = array(
arry=array(a,“1234”);
r
e
s
u
l
t
=
s
e
r
i
a
l
i
z
e
(
result = serialize(
result=serialize(arry);echo $result.“
”;
//正常payload:
a:2:{i:0;O:1:“a”:1:{s:1:“a”;s:3:“123”;}i:1;s:4:“1234”;}
//删除末尾花括号payload:
a:2:{i:0;O:1:“a”:1:{s:1:“a”;s:3:“123”;}i:1;s:4:“1234”;
//数组对象占用指针payload(加粗部分数组下标和前面重复都是0,导致指针出问题)
a:2:{i:0;O:1:“a”:1:{s:1:“a”;s:3:“123”;}i:0;s:4:“1234”;}
其余GC回收机制利用
也叫 php issue#9618
版本条件:
- 7.4.x -7.4.30
- 8.0.x
----------------------------------------------------------------------
**属性键的长度不匹配:
**
//正常payload
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:4:“Aend”;s:1:“1”;}
//外部类属性键长度异常payload:
//先外类__destruct()后内类__wakeup()
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:6:“Aend”;s:1:“1”;}
----------------------------------------------------------------------
属性值的长度不匹配:
//正常payload
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:4:“Aend”;s:1:“1”;}
//外部类属性值长度异常payload:
//先外类__destruct()后内类__wakeup()
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:4:“Aend”;s:2:“1”;}
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N;}s:4:“Aend”;s:1:“12”;}
----------------------------------------------------------------------
去掉内部类的分号:
注:
- 这样内部类直接回收,外部类没事,可以直接不执行内部类的wakeup。
- 外部类去掉分号同理。
- 如果内部外部类的花括号紧贴,也可以在两个花括号中间加分号,可绕过内部类wakeup。
//正常payload
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N**;**}s:3:“end”;s:1:“1”;}
//去掉了内部类的分号的payload
O:1:“A”:2:{s:4:“info”;O:1:“B”:1:{s:3:“end”;N}s:3:“end”;s:2:“1”;}
注:使用前提是分号前面这个数据不可以是payload,否则将导致payload无法识别而被抛弃,如果它是一些无关紧要的数据,那就可以随便丢。
GC回收机制的总结
本质上,上面这些 对象的属性数量大于真实值、fast-destruct、其余GC回收机制利用 三个板块都是同一个东西同一个原理。
想要不执行wakup,就必须在有wakup魔术方法的那个类的结构进行破坏,可以采用删除分号或者属性数量不一致的方法。
在存在destruct且恶意方法在destruct情形下的链子,wakup是完全无效的,它不但可以被绕过,甚至可以不被执行
绕过weakup
参考文章:
官方wp
CTfshow 卷王杯 easy unserialize(特详)_Jay 17的博客-CSDN博客
PHP的GC垃圾收集机制 - 简书 (jianshu.com)
php反序列化之绕过wakeup – View of Thai
PHP反序列化中wakeup()绕过总结 – fushulingのblog
[绕过__wakeup,先执行外面的类&天翼杯的eval那个题的一个思考_Je3Z的博客-CSDN博客](