关于php反序列化链的构造以及application/x-www-form-urlencoded的一小点细节
今天复盘了一道php反序列化构造链的题目
昨天试手了一下dasctf春季赛,接触了这道php反序列化构造链的题目
其实之前也接触过一次,但一直没来得及总结……
题目的源码如下
1 |
|
首先我们要知道一点,这是利用反序列化进行攻击,我们最终的目标就是执行get_flag
函数当中的eval函数,eval
函数可以让我们命令执行,以此读取文件,但是我们需要顺着反序列化链的顺序,然后顺着链的执行顺序,最后来到eval
函数
那么我们应该先把反序列化类和对象的调用顺序找出来,在此之前,我们得明白这些带有__
(双下划线)的魔术方法的用处以及什么时候会调用它,注意:因为序列化和反序列化只会对类中的属性(对象)生效,所以我们重点要操控的是类的属性并利用魔术方法进行攻击(或是调用你想要调用的函数)
魔术方法的说明可参考上述参考博客https://pankas.top/2022/03/01/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E7%AE%80%E5%8D%95%E6%80%BB%E7%BB%93/
__destruct
在一个对象销毁的时候调用,在php完成反序列化你的序列化的数据之后就会销毁你传入的对象,那么这个魔术方法就是第一个被调用的在
__destruct
当中,$this->f1与'114514'
(你是一个一个一个魔术方法啊啊啊)的字符串拼接在了一起__toString
的魔术方法会在一个对象被当作字符串处理的时候触发在
__toString
当中调用了run()
函数,但是这里有2个run
函数(后面发现是哪个都没关系)接下来
__invoke
魔术方法就被调用了(当一个对象被当作函数调用的时候就会触发该魔术方法)在
__invoke
当中调用了world()
函数,但是world
函数是不存在的方法这会触发
__call
魔术方法(当调用一个不存在的函数方法的时候该魔术方法会被触发)__call
魔术方法有两个参数,一个是方法名,另一个是传进方法的各个参数但是这2个参数在这道题里面没有什么作用,接下来会调用
get_flag
函数正好
get_flag
当中有我们需要用到的eval
函数,但是问题来了,一开始我并不明白这最上面的eval
函数是干什么的,后来才发现这是用来迷惑人的……
1 | public function get_flag() |
这个eval
当中的参数加上了#
(注释符),因此你执行的命令会被注释掉,起不到效果
但是其实是可以加上一个\n
换行符的,因为#
只会对一行的字符串生效(注意eval
当中的命令还要加一个’;’,不然会报错)
那么反序列化链的顺序就很清楚了
1 | fin::__destruct=>what::__toString=>crow::__invoke=>fin::__call =>mix::get_flag |
payload如下
1 | class crow |
如何构造这样的链:首先得明白是一个类当中的哪一个对象会触发下一个函数方法或是魔术方法,然后需要用触发的类来给这个对象赋值(比如fin
类当中的f1
对象触发了what
类当中的__toString
的魔术方法,由于之前说的——反序列化和序列化不会对函数方法生效,所以我们只要操控类和类当中的变量就可以了,用what
类给fin
类中的f1
赋值,这样一来序列化的链就构造完成了)大概就是根据方法的调用顺序,自己构造一个和方法调用顺序相同的逻辑链,顺着这个逻辑走下去就可以调用你想要调用的函数了(还有,序列化和反序列化的顺序都是从外到里的)
这里有一个错误示范
1 | $fin = new fin(); |
这样看上去并没有什么问题(只是使用了同一个变量代表的类重复赋值),但是urlencode
的时候就会发现数据有明显削减的地方,这是怎么回事呢?
我根据”赋值即为构造链”的特性研究了一下
1 | 你以为的效果:fin::f1 => what::a => mix::m1 => crow::v1 => fin::f1 => mix::m1 |
建议在多次调用的时候避免重复赋值,以免对反序列化链造成破坏,例如用new fin()
定义两个不同的对象$fin
和$fin1
那个时候我和队友用payload使用hackbar传数据,但是没有什么响应
但是将enctype改成application/x-www-form-urlencoded(raw)
的时候成功了(flag在html元素的注释里)
注意在这个过程中不要使用hackbar的decode工具然后再encode回去,它会对你数据当中的某些特殊字符(例如\n)做特殊的处理,导致payload不生效
用bp的话也可以
在content-length为373的请求中(x-www-form-urlencoded(raw))
得到了flag
而在content-length为372的请求中(x-www-form-urlencoded)
没有得到flag
查了一下它们两者之间的区别