PHP反序列化(Pikachu)
原理
php程序为了保存和转储对象,提供了序列化的方法。php序列化是为了在程序运行的过程中对对象进行转储而产生的。序列化可以将对象转换成字符串,但仅保留对象里的成员变量,不保留函数方法。
php序列化的函数为serialize
,可以将对象中的成员变量转换成字符串。
反序列化的函数为unserilize
,可以将serialize
生成的字符串重新还原为对象中的成员变量。
将用户可控的数据进行了反序列化,就是PHP反序列化漏洞。
PHP反序列化漏洞详解(万字分析、由浅入深)_php反序列化漏洞原理-CSDN博客
[最通俗易懂的PHP反序列化原理分析 - FreeBuf网络安全行业门户
[反序列化漏洞原理、成因、危害、攻击、防护、修复方法_jdbc反序列化获取服务器权限的危害-CSDN博客](https://www.freebuf.com/articles/web/167721.html)
序列化serialize()
序列化说通俗点就是把一个对象变成可以传输的字符串,比如下面是一个对象:
class S{
public $test="pikachu";
}
$s=new S(); //创建一个对象
serialize($s); //把这个对象进行序列化
序列化后得到的结果是这个样子的:O:1:"S":1:{s:4:"test";s:7:"pikachu";}
O:代表object
1:代表对象名字长度为一个字符
S:对象的名称
1:代表对象里面有一个变量
s:数据类型
4:变量名称的长度
test:变量名称
s:数据类型
7:变量值的长度
pikachu:变量值
反序列化unserialize()
就是把被序列化的字符串还原为对象,然后在接下来的代码中继续使用。
$u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}");
echo $u->test; //得到的结果为pikachu
序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数,就会导致安全问题
常见魔术方法
__construct() //类的构造函数,创建对象时触发
__destruct() //类的析构函数,对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //读取不可访问属性的值时,这里的不可访问包含私有属性或未定义
__set() //在给不可访问属性赋值时触发
__isset() //当对不可访问属性调用 isset() 或 empty() 时触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当尝试以调用函数的方式调用一个对象时触发
__sleep() //执行serialize()时,先会调用这个方法
__wakeup() //执行unserialize()时,先会调用这个方法
__toString() //当反序列化后的对象被输出在模板中的时候(转换成字符串的时候)自动调用
触发条件
unserialize函数的变量可控,php文件中存在可利用的类,类中有魔法函数
防御
●最有效的方法是不接受来自不受信任源的序列化对象或者只使用原始数据类型的序列化,但这不容易实现。
●完整性检查,如:对序列化对象进行数字签名,以防止创建恶意对象或序列化数据被篡改。
●在创建对象前强制执行类型约束,因为用户的代码通常被期望使用一组可定义的类。
●记录反序列化的失败信息。比如传输的类型不满足预期要求或者反序列化异常情况,因为这有可能是攻击者的攻击尝试。
pikachu
打开题目就一个输入框,那应该就是概述里的举例
那先用解说中的payload(O:1:"S":1:{s:4:"test";s:29:"";})
那来分析怎么构造这个payload
PHP源码:
class S{
var $test = "pikachu";
function __destruct(){
echo $this->test;
}
}
$s = $_GET['test'];
@$unser = unserialize($a);
入口:@$unser = unserialize($a);
魔术方法:__destruct()
魔术方法的触发条件:对象被销毁时触发
过程:
创建一个对象——>销毁时触发__destruct()魔术方法——>打印 this->test变量
new S() -> __destruct() -> $this->test
所以我们可以通过控制$this->test
的内容来构造恶意数据
打开PHP在线工具PHP 在线工具 | 菜鸟工具 (jyshare.com)
构造payload
<?php
class S{
function __destruct(){
echo $this->test;
}
}
$a=new S();
$a->test="<script>alert('hack')</script>";
echo serialize($a);
O:1:"S":1:{s:4:"test";s:30:"<script>alert('hack')</script>";}<script>alert('hack')</script>
成功