序列化与反序列化漏洞
承接上面的例子,可以有一个这样的猜测,如果我们将序列化的字符串按格式要求随意更改,是不是可以控制intro()方法的打印信息?答案是肯定的。事实上,序列化与反序列化漏洞也正是由此而来。(简单提一下)
假设有这样一个环境:
存在url:
127.0.0.1/ser.php
其对应的后台代码如下:
<?php
class person {
public $name="echo 'I am isee'";
public function __wakeup(){
eval ("$this->name;");
}
}
if (isset($_GET['mid'])){
$mid=$_GET['mid'];
unserialize("$mid");
}
else{
echo "<h1>hello!!!<h1/>";
}
//如果传递参数把参数进行反序列化,然后代码执行
//如果没有,直接打印hello!!!
如果带上?mid
参数服务器后端对应的PHP代码将会反序列化这个用户传参,此时如果我们在本地正常序列化这个**person类
并拼接到?mid
的结果是这样的
本地正常序列化**person类
:
<?php
class person
{
public $name = "echo 'I am isee'";
public function __wakeup()
{
eval ("$this->name;");
}
}
$person1=new person();
$mid=serialize($person1);
var_dump($mid);
//序列化后的字符串
//O:6:"person":1:{s:4:"name";s:16:"echo 'I am isee'";}
url拼接mid
参数后访问服务器:
可以清楚的看到,传递序列化后的字符串成功的被后台的unserialize()
还原成了原本的person类
,从而触发了__wakeup
魔术方法,最终eval()
成功的将$name
的值 echo 'I am isee'
将PHP代码解析,输出了I am isee
字样
如果我们自定义这个序列化后的特殊字符串,会怎样?
可以看到,ping命令被成功的执行,这也说明,其他的任意命令也可以执行了。
因此,简单小结一下PHP序列化与反序列化漏洞的成因:
序列化的字符串可以保存类的基本信息,反序列化的过程可以将这个特殊的字符串重新还原成类,虽然在整个序列化与反序列化的过程中我们无法控制类方法的改变(这个主要指后台的自定义函数),但是我们却可以通过复写变量并借用类中自定义好的方法(服务器上的)或魔术方法(服务器存在的或本地自定义的),并借用敏感函数来达到恶意效果。
关键点就在于PHP序列化与反序列化的过程用户可控。
常见需要注意的魔术方法
本次讨论如下几个魔术方法:
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发