PHP反序列化漏洞

什么是序列化

我们在运行代码的时候一个对象在代码结束的时候就被销毁了,我们如果在另一个地方要调用的话还要重新去声明,如果我们将这个对象序列化成一个字符串储存,在要调用的时候再把它反序列化出来。

我们定义的类里面的变量是有自己的属性的。

PHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的,字母对应的类型

  • public(公有):公有的类成员可以在任何地方被访问。
  • protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问。
  • private(私有):私有的类成员则只能被其定义所在的类访问。
1
2
3
4
5
6
7
8
9
10
11
a - array  数组
b - boolean 布尔型
d - double 双精度型
i - integer 整型
r - reference 对象引用
s - string 字符串
C - custom object 自定义对象序列化
O - class 对象
N - null NULL值
R - pointer reference
U - unicode string

序列化的实例

在这里我们把file的值定为flag.php,但是因为这里的$file是privat形式的所以我们在写脚本的时候要将flag.php放到()里面,同时这里因为是private类型的变量在反序列化的时候在变量名的长度是会有两个空格的,在你生成代码后直接复制是不行。

输出为:O:+4:”Demo”:2:{s:10:”Demofile”;s:8:”fl4g.php”;}因为这里有一个base64的解码我们直接在这里加密了

说以真正输出是TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQo=

PHP魔法函数

常见的魔法函数

1
2
3
4
5
6
7
__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。
__destruct: 和构造函数相反,当对象所在函数调用完毕后执行。
__toString:当对象被当做一个字符串使用时调用。
__sleep:序列化对象之前就调用此方法(其返回需要一个数组)
__wakeup:反序列化恢复对象之前调用该方法
__call:当调用对象中不存在的方法会自动调用该方法。
__get:在调用私有属性的时候会自动执行__isset()在不可访问的属性上调用isset()或empty()触发__unset()在不可访问的属性上使用unset()时触发

反序列化漏洞的成因大多就是我们定义的类对象中的函数中存在危险指令被利用而导致的,而这些特有的函数是可以被重写的,这里就是可以利用这些函数去构造pop链,然后触发敏感函数执行我们的恶意代码,反序列化漏洞的出现一般就是程序员对魔法函数的触发没有考虑好出现的。

tips

绕过_wakeup函数:当我们的设置的属性的个数大于实际的个数的时候wakeup就可以做到绕过上面那个题目就是把xctf中的属性值改为2就好了,这是CVE-2016-7124适用版本

PHP5 < 5.6.25

PHP7 < 7.0.10

绕过正则匹配:在反序列化后的数据的O:4的4前面加上一个+号就能够绕过这个,这是PHP的bug参考:https://www.phpbug.cn/archives/32.html

​ 复现

有关骑士CMS<6.0.48的任意文件包含漏洞

在这里看到骑士的版本是6.0.20然后在网上去找到了方法 http://127.0.0.1/74cms/upload/index.php?m=home&a=assign_resume_tpl 这个是漏洞地址

我们要将ariable=1&tpl=“)?>; ob_flush();?>/r/n<qscms/company_show 列表名=”info” 企业id=”$_GET[‘id’]”/>一post形式传参过去

然后包含日志文件

post传variable=1&tpl=data/Runtime/Logs/Home/22_03_02.log完成操作

同样我们可以放

variable=1&tpl=“)?>; ob_flush();?>/r/n<qscms/company_show 列表名=”info” 企业id=”$_GET[‘id’]”/>这段代码这样就可以在保含日志文件的时候将这段代码给执行写一个后门上去

当我们的concat被过滤的情况下sql注入该怎么办呢

make_set用法:在mysql中使用make_set,函数的第一个参数为字符串,后面的可以理解为回显位。当第一个参数为1时,根据其二进制的反过来的值,如果对应后面的位置为1的,将1上的内容显示出来:1的二进制为0001,反过来为1000,因此显示的为1所在位置的值a:那么如果是3的话二进制是0011反过来就是1100就会把前两个给输出

这个题目需要用到make_set()函数make_set(1,’a’,’b’,’c’)返回的是那我们的思路就是利用报错函数(这里把union 也过滤了就不用联合查询了)and updatexml(1,make_set(‘2’,’~’,(select database())),1)这里之后就直接往下做就好了。