1-1这是啥?
序列化:将对象转换成数组字符串格式的数据 serialize()
反序列化:将字符串数组数据转换成对象 unserialize()
以下内容为将user类中的数据经过序列化操作后输出,创建一个对象后不对变量赋值会默认为类中定义的值
还有反序列化操作,变回原来数据的样子,var_dump函数是将对象内容输出
魔术方法
反序列化涉及到许多魔术方法,他们在各种情况下有着属于自己的触发规则
_construct _destruct
前面还有一个_construct没截取到,在创建完test对象后会执行_construct()函数,于是输出了它的内容,然后使用unset(),这个是销毁函数,会调用_destruct,输出内容。本次属于主动调用,不调用这个unset()函数他也会执行_destruct()
程序结束后看到没有调用unset()函数,他还是会调用_destruct来销毁
_sleep _wakeup
在类外部调用序列化函数serialize()时会调用_sleep()方法
反之,在类外部使用unserialize()会调用_wakeup()函数
_invoke()函数
以调用函数的方式调用对象会执行_invoke
_tostring()
用调用字符串的方式调用对象,会执行_tostring()
_call()
调用不存在的方法时,会调用_call()函数
_get()
访问不存在的变量时会调用
_set()
为不存在的属性赋值的时候会调用
_isset()
在对不是public成员调用isset或者empty时,会调用_isset(),isset或者empty这俩的返回值对于不是public成员为1
_unset
在类外部使用unset函数来删除某个成员的时候调用_unset()
案例
假如以下php文件是挂在web上的,如何才能利用反序列化执行命令?
因为他是通过get传递一个x变量,然后反序列化里面的内容。看到类中存在_destruct,想到如果能创建一个B类的对象,调用完反序列化函数unserialize()后会执行_destruct,也就能执行system()。刚好这里的x变量我们可以通过url构造成创建一个B类对象的样子,那么就出来了
于是创建一个B类对象,对他序列化,然后放到url中。对应文件先反序列化得到数据,发现是一个B类对象后没有后文便销毁,销毁调用_destruct()执行命令
换掉cmd变量为ver也可以
1-2公共类型-public private protect
对于类成员定义的不同类型,大致分为public private protect三种,其中public随便访问,protect只在本类和子类中访问,private只在本类访问。
观察下面的图片,可以看到对于不同类型所产生的反序列化数据是不一样的,private需要加上%00类名%00,protect会加上%00*%00
cve利用
那么反序列化漏洞是怎么利用的呢?
我们从前面的知识点中了解到,在调用反序列化函数的时候会先调用_wakeup方法,那么以下的cve就讲述了如何绕过_wakeup方法
也就是说绕过方法为修改该对象中变量个数,造成3>2的情况
此cve可以参考buuctf中的题目
通过目录扫描工具可以下载一个zip文件
里面是一些php文件,在class文件中可以看到flag被包含
想要输出flag就得让username=admin,password要等于100,但是他会有一个_wakeup方法在创建对象的时候,修改username为guest,于是查看php版本是否为对应漏洞版本
在index.php中包含了class.php,获得了一个select参数,并将它反序列化
那么思路就是利用好漏洞,将username=admin,password=100,将其序列化后的数据修改变量个数提交就好
O%3A4%3A%22Name%22%3A2%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bi%3A100%3B%7D
修改变量为3个
O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bi%3A100%3B%7D
然后就好了
字符串逃逸
对于反序列化的防御来说,以上面的靶场为例,我们只需要在命名username=admin的时候,限制其为admin,或者说将它转义成其他内容,导致不能将代码执行到想要的那一步去
以下内容便使用到了过滤操作,将序列化得到的数据中的admin过滤成hacker,可以看到想要将其反序列化后输出该数据是false,说明是不行的
那么被过滤后如何才能让他成功反序列化后输出数据呢?为了解决这个问题,我们需要使用以下操作
增加逃逸
查看替换后增加了多少数据,这里增加了一个,从5变成6,那么换掉长度为后面的所有数据长度/1,如果增加了两个,换掉长度为后面数据长度/2
。将这些字符用被过滤的字符串替换
思路:因为admin被替换成了hacker,对应字符5变成了6,导致后面全部数据无法被解析。那么就将admin后面的所有数据看成单独个体,将这些个体全部替换(用admin来代替这些个体)。这样反序列化解析的时候就会一直解析到admin的结束位置,再拼接上原来的数据,就变成了一组新数据。
在admin后面可以看到一共存在47个字符,那就有47个单独个体,47*admin=47*5,加上单独个体的总数47*5+47变成47*6
以下便为47个admin和后面的47个字符
因为刚好hacker是六位,在被调用替换函数后,47个hacker替换掉admin后多占用了47个位置,就把原来的47个单独个体的位置覆盖了,那么就会为原来的47个单独个体开辟新的空间去读取
将数据经过序列化然后过滤后反序列化使用var_dump可以解出来
减少逃逸
如果是从admin变成了hack,5->4,又该如何解决呢?
因为是减少,所以整体数据需要向前移动几个单位,对于整段数据,在被替换字符admin的后面,固定的点在password字段,所以这次将需要替换的部分截取到该处。
此处共计22个字符,那么就填充22个admin,输出替换后的结果如上图所示
但是很明显光凭hack是凑不够110个字符的,他只有88个,即使反序列化后还是输出不了。可是我们可以控制password的内容,凑够那剩下的22个字符
那么就将后半部分的内容给password字段
整个操作有点像构造闭合的sql语句。总结就是遇到增加,后面有多少个字符就多少个admin,然后再把替换的内容填上去。遇到减少,找到离被替换的字符最近的字段的位置,有多少个字符就替换多少,然后再把后面的部分构造成第二个字段参数。这些都只是只增加或减少一个的情况下。
php原生类
php原生类是指php内置的类,它可以直接在php代码中调用无需安装其他库。这些类可以手动关掉。为什么可以利用原生类反序列化呢?因为在这些类中也使用到了前面提到的各种魔术方法,所以可以“借刀杀人”
在不同php对应版本中可以利用的原生类存在差异,使用脚本可以跑出你所使用的php版本调用了哪些原生类,在这些原生类中又调用了那些提到过的魔术方法
在以下php代码中,不能很直观的看出使用了那些魔术方法,但其实在echo语句部分,把对象当成字符串输出调用了_tostring()方法。
既然使用到了_tostring(),按照注释给出的步骤,先查当前php版本对应着哪些类使用了_tostring()
第一个exception类,也叫异常类使用到了
于是创建一个异常类对象,构造xss的反序列化数据
将产生的pop链给参数k实现跨站
原生类应用
以ctf的题目为例,根据以下图片,我们需要构造xff为127.0.0.1,token=ctfshow才能获取到flag。
查看源码,看到使用到了反序列化,用$vip去触发getFlag。但是在这里getFlag方法并没有声明,说明调用了不存在的方法,调用不存在方法会使用到_call()的魔术方法
使用脚本发现对应php版本中Soapclient用到了该魔术方法
于是就构造pop链,根据SoapClient类规则添加构造参数输出他的反序列化数据
把数据赋值给$vip,刷新页面得到
1-3 面对框架的pop链构造
前面内容的代码都是很简单的一段,是个人来都看得懂操作逻辑,构造pop链也很清楚,因为参数就那么几个。但对一个项目来说,工作量就很巨大了,你得要清楚代码逻辑,还得看得懂。。。当然网上也有很多poc教程,但是可能存在项目不一样,版本不一样等多种问题他能写出来我不行。。。
对于框架,接下来介绍两款工具帮我们写pop链。第一款需要配合iis搭建,第二款只能安装在linux服务器上。将第一个的环境搭建放在后面了
那么在我们遇到使用到框架的情况下,以下面的靶场为例,该怎么去写它的pop链?
目录扫描得到文件然后审计 网址+www.zip,在文件中看到框架版本
找到index.php看请求方式,img是刚刚看到的图片。
通过get传递的payload,主要要跳到unserialize一步反序列化payload。按照常规方法来做,使用到unserialize,肯定会用到_wakeup()方法,但这里面没写。考虑原生类,也没有明显的。。。
还存在过滤,需要添加两个//来绕过正则表达式
于是使用第二个项目去构造pop链,先查看爆出过哪些漏洞,根据对应版本我选择rce3
环境搭建
安装iis服务器和程序开发组件
新建网站,将下载好的源码放在物理路径
修改访问权限为everyone,给完全控制权限
访问就好了
和phpggc功能一样,只不过在这里面使用php功能的时候会受限,因为主要的phpggc适用在linux上
Comments NOTHING