0X01
遇到个题目代码如下
error_reporting(0); class { public $cmd='index.php'; public function __destruct(){ if (preg_match('/w+((?R)?)/', $this->cmd)){ eval('$a="'.$this->cmd.'";'); } else { die('hack!!!'); } } } if (!isset($_GET['fl']) || !isset($_GET['ag'])) { die(@highlight_file('index.php',true)); } else { if (!(preg_match('/[A-Za-z0-9]+(/i', $_GET['fl']))) { die('hack!!!'); } else { echo unserialize($_GET['ag']); } }
首先 这种题一看就知道是反序列化的题型,我们来逐一分析代码。
0x02
这里定义了一个flag类并且有一个属性$cmd还有一个php反序列化后会自动执行的__destruct方法
关键点在于
eval('$a="'.$this->cmd.'";');
这里就是命令执行的关键了,假定我们自己序列化了一个对象,$cmd里写入命令fuckyou 会被拼接成$a="fuckyou";
而eval后运行没什么卵用 仅仅是$a被赋值成了我们的$cmd属性值
参考eval函数,当内容里用分号分割时可以多条命令同时执行
所以我们传入的属性值需要闭合$a="这里的命令 也就是输入";我们要写的命令
后面同理 闭合之后的";可以写成";我们要写入的命令;$b="
这样 eval函数里的参数会变成eval($a="";我们要写入的命令;$b="");
这样当eval函数执行的时候就会同时运行 $a="" 我们写入的命令 $b=""了!
0x03
所以本题的解题思路是 传递一个flag对象的序列化内容 cmd属性值写入上面那个payload然后让__destruct函数执行即可
然后看主体部分
if (!isset($_GET['fl']) || !isset($_GET['ag'])) { die(@highlight_file('index.php',true)); } else { if (!(preg_match('/[A-Za-z0-9]+(/i', $_GET['fl']))) { die('hack!!!'); } else{ echo unserialize($_GET['ag']); } }
这里如果没有赋值fl和ag的get参数就会代码高亮显示
如果赋值了进入else 正则匹配fl参数 如果fl参数内容不包括A-za-z0-9这些数字字母和(左括号,程序停止
如果匹配了就反序列化ag参数的内容
所以我们需要给定一个fl参数的内容为A(即可
也就是 ?fl=A(&ag=序列化后的对象
0x04
序列化的对象怎么构造,这里又需要看到一处
if (preg_match('/w+((?R)?)/', $this->cmd))
这里会递归匹配$cmd属性内()中的内容,需要让$cmd属性值内无参数
这是构造序列化对象的步骤,这里需要加一个array(),原因是题目中是直接echo出来内容的
但是phpecho对象会直接500错误 放到数组里就可以
成功执行了phpinfo
0x05
但是执行了phpinfo有什么用呢,最终目的是获取flag.php中的内容
但是又必须要使得我们输入命令的那一块无参数
phpinfo中可以发现 是apache2.0的服务器web引擎
参考:
http://docs.php.net/manual/tw/function.getallheaders.php
所以可以新建一个序列化对象
带有getallheaders()函数 ,获取所有headers头部放入数组,然后通过end函数获取数组的最后一个值,在通过system函数执行即可
这里end获取头部的数组最后一个键值对后,拿到了ls这个值并system执行,成功列出目录,然后cat flag就能拿到flag了