这个很重要
为什么会产生这个东西:序列化之后便于我们对象的传输和保存,这个作用就是为了数据的传递和格式的转换,我们称之为序列化。
在这给过程中,会涉及到一种叫做有类和无类的情况,开发里面经常看到的一个东西,我们称之为类,类对象,存在类的话,在类里面有一些内置的魔术方法,如果有类用到反序列化这个操作,就会触发到里面的魔术方法,有时候在不经意之间就会和其他漏洞相互组合。
无类情况,没有在代码写类,就不会触发到魔术方法,这个要看具体代码里面有没有
利用:1.在真实应用,网站下面,2.各种ctf大赛里面经常出现
反序列化的安全问题,会造成漏洞结合使用,他就会产生sql注入,代码执行,目录遍历等等。
相关的开发语言进行序列化就会转换成二进制,xml,json数据,这种数据反序列就会转换成开发语言,代码形式。
数据格式类型的相互转换
序列化就是对象转换为字符串,反序列化就是字符串转换为对象。
#php反序列化
原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,sql注入,目录遍历等不可控的后果。在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。
这就会涉及到两个函数:serialize() //将一个对象转换成一个字符串 ,序列化
unserialize()//将字符串转换为一个对象。反序列化
出发的原因的就是unserialize()使用这个函数时候,存在变量可以控制,触发类里面的魔术方法。
##先搞一把PHP反序列化热身题稳住-无类问题-本地
一个简单的序列化代码
这个输出结果是什么意思,s是字符串型,变量长度9,变量名字就是xiaodi123,
这是无类的一种情况。而把输出的值换到变量key,然后使用unserialize()这个函数,一个简单的反序列化代码看看
只有使用他对应的格式,才能被反序列化,还原出来。
如果代码里面等于123,str该等于是什么,数字的话输出结果就是这种
如果在123用2“双引号括起来,就还是会以字符串模式显示。
##在橹一把CTF反序列化小真题压压惊-无类执行-实例
这里提示输入账户密码之后点登录没有反应,可能要换一个提交方式
提示hint
所以在网站上加上?hint等于个字符串,再去访问网站
返回的源代码,然后从代码里面分析有什么漏洞,
我们的思路加上让传输的hint值反序列化之后和key的值相等于,就能输出flag,所以这里hint就等于的序列化值,因为代码要cookie提交方式,所以抓住他的数据包,对cookie做修改
发送数据包出去,网站并没有任何反应
这是因为代码里面上面,有一个先接受get的传输的hint值,而我们刚刚的数据包里面hint传输至了,所以就不会执行下面的语句,这时候应该是把hint取掉,在抓一次数据包,然后修改cookie值,
放出去之后,出来登录框
还是不可以。
这是因为代码执行有个先后顺序,而key的赋值在后面,比对在前面,所以在前面比对的时候,就比对的key值是空的,
既然key的值比对的时候是空的,所以在cookie值哪里就应该比对是空的反序列化值
在发送出去数据包,flag就出来了
所以这一题就有两个陷阱,第一个是if,hinnt值判断,第二个就是代码的执行顺序,
下面开始反序列化里面的精髓一个简单的有类代码
魔术方法
在特定情况下会自动触发的方法
触发:unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法
参考:https://www.cnblogs.com/20175211lyz/p/11403397.html
__construct() //创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
这个代码就是有类的,这里新建了一个对象,变量a,变量a新建等于新的abc,新的abc是新建的就会触发调用构造函数,只要是新建的对象就会调用function_contstruct这个魔术方法;他就会在不经意被调用,这个代码就会被执行。
而序列化,并没有调用下面的函数
反序列化,就触发调用了苏醒函数,wakeup,
而对象死的时候,下面并没有写出发函数,但还是触发了一个析构函数,destrust,这就关系到了上面写的 __destruct() //对象被销毁时触发 这个了。程序结尾之后会默认调用这个结尾,就会调用那个函数
通过这个实例,在这个类写法里面涉及到序列化,对象被创建,执行完等等,就需要知道魔术方法在什么样的场景下面会被触发;有类和无类的区别就是有没有魔术方法,而魔术方法会在特定的情况下被触发。
就是魔术方法需要对应的方法触发,才能执行魔术方法,比如构造函数就要创造一个函数。
##顶一把网鼎杯2020青龙大真题舒服下-有类魔术方法触发-实例
打开靶场之后就直接显示出代码
class明显的是个有类的,代码里面调用了__construct(),__destruct()两个魔术方法。
解题思路
首先:根据题目名称及代码中的unserialize函数判断考点是反序列化知识点
第一:程序包含了一个flag.php,显然flag在其中
第二:程序包含两个魔术方法__destruct、__construct
第三:传输str参数数据会触发__destruct,存在is_valid过滤
第四:__destruct会调用process函数,在process函数中,当op=1执行写入函数,当op=2执行读取函数
第五:代码中有类FileHandler,其中3个变量op、filename、content。根据题目要求构造需要的序列化字符串
第三步里面为什么会触发__destruct,因为进行了反序列化,所以触发了__destruct函数,调用了之后
网上说可以把->理解的或者他的
就赋值变量的op,如果op的类型和值都等于2就会被强制转换成1,而contens声明为空,process就是定义的一个函数,在上面有
如果变量this的do等于1,就用变量this的write,写入函数
如果变量this的do等于2,就用变量this的read,读取函数
,
这个代码的顺序就是先赋值给变量str,变量str进行反序列化操作,然后默认会调用__destruct魔术方法,魔术方法就会调用process这个函数,然后这个函数会根据op的值选择写入还是读取。
获取flag还是读取好,触发读取方法就是让op值等于2,但是在process执行的时候会有一个op=2时强制转换为op=1的过滤,防止调用读取;
而这里涉及到=号如果,他是三个=号,就会比对类型和值,就可以写为" 2",为什么是双引号里面里面括起来给空格喝,因为他会比对类型是字符串,就绕过了,空格2,值就对应不上了,所以绕过了。
这个代码的大概率流程,赋值给str,反序列化触发魔术方法,下面有一个op值滴鼻过滤,===(类型和值),传参op=“ 2”,字符串类型,代码里面是“2”,因为有空格所以不成立,就不会转换为1,之后就回到process来判断op值,==弱类型对比,之前传参过滤” 2“,两个==验证值,是2,所以成立,之后就会到读取里面。
取这个变量filename值,然后用下一行那个函数去读取,然后进行一个返回
在最上面已经声明变量filename,,就可以给他赋值,
然后就是到最后一步传递的参数值计算,因为是反序列化,所以要给值序列化一下,代码如下
<?php
class FileHandler{
public $op = " 2";
public $filename="flag.php";
}
$a = new FileHandler();
echo serialize($a);
//输出: O:11:"FileHandler":2:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";}
?>
serialize是序列化,就是把这两个值序列化输出的结果,传递给变量str,他反序列化就能解析出来,输出的结果,传递给str
右键查看源代码
##然后抗一把CTF反序列化练习题围观下-有类魔术方法触发-本地
ss