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

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

缺陷编号:wooyun-2016-0170003

漏洞标题:上海格尔安全认证网关管理系统命令执行漏洞大礼包

相关厂商:cncert国家互联网应急中心

漏洞作者: xfkxfk

提交时间:2016-01-15 11:52

修复时间:2016-04-11 16:08

公开时间:2016-04-11 16:08

漏洞类型:命令执行

危害等级:高

自评Rank:20

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

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2016-01-15: 细节已通知厂商并且等待厂商处理中
2016-01-19: 厂商已经确认,细节仅向厂商公开
2016-01-22: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2016-03-14: 细节向核心白帽子及相关领域专家公开
2016-03-24: 细节向普通白帽子公开
2016-04-03: 细节向实习白帽子公开
2016-04-11: 细节向公众公开

简要描述:

上海格尔安全认证网关管理系统命令执行漏洞多处

详细说明:

第一和第二处命令执行
文件/kssl/kssl/WEBUI/www/api/service.php

<?php
include_once "../global/common.php";
include_once "../ssl/service_helper.php";

/* 处理GET请求 */
$service_path = $_GET['service_path'];

switch( $_GET['service_action'] )
{
case 'start': {
if ( true == start_service($service_path) ) {
$retry_limit = 10;
$state_expected = '已启动';
WEBUI_log( LOG_INFO, "启动代理服务$service_path"."成功" );
}
else {
WEBUI_log( LOG_INFO, "启动代理服务$service_path" );
}
while( $service_path != "" && $retry_limit > 0 ) {
$state = status_service($service_path);

/* 如果HRP状态达到了期望值,则中止重试操作 */
if( $state == $state_expected ) {
break;
}
else {
sleep(1);
$retry_limit--;
}
}
break;
}
case 'stop': {
if ( true == stop_service($service_path) ) {
$retry_limit = 10;
$state_expected = '已停止';
WEBUI_log( LOG_INFO, "停止代理服务$service_path"."成功" );
}
else {
WEBUI_log( LOG_INFO, "停止代理服务$service_path" );
}
while( $service_path != "" && $retry_limit > 0 ) {
$state = status_service($service_path);

/* 如果HRP状态达到了期望值,则中止重试操作 */
if( $state == $state_expected ) {
break;
}
else {
sleep(1);
$retry_limit--;
}
}
break;
}
case 'download': {
$proxy = get_proxy($service_path);

$usermap_url = $proxy['usermap_url'];
if( WEBUI_exec( "$SSL_DIR/bin/hrp-download-usermap.sh $SSL_DIR/cfg/$service_path", true ) ) {
WEBUI_log( LOG_INFO, "从$usermap_url"."下载代理服务$service_path"."的用户映射策略成功" );
}
else {
WEBUI_log( LOG_ERR, "从$usermap_url"."下载代理服务$service_path"."的用户映射策略失败" );
}
$acl_url = $proxy['acl_url'];

echo "<script language='JavaScript'>";

if( WEBUI_exec( "$SSL_DIR/bin/hrp-download-acl.sh $SSL_DIR/cfg/$service_path", true ) ) {
WEBUI_log( LOG_INFO, "从$acl_url"."下载代理服务$service_path"."的ACL策略成功" );
echo "window.alert(\"激活策略成功\");";
}
else {
WEBUI_log( LOG_ERR, "从$acl_url"."下载代理服务$service_path"."的ACL策略失败" );
echo "window.alert(\"激活策略失败\");";
}
echo "window.close();";
echo "</script>";
break;
}
case 'user': {
if ( '已启动' != status_service($service_path) ) {
echo "服务未启动";
break;
}

$proxy = get_proxy($service_path);
$mrtg_enable = $proxy['mrtg_enable'];
$mrtg_ip = $proxy['mrtg_ip'];
$mrtg_port = $proxy['mrtg_port'];
if ( $mrtg_enable == 'On' ) {
system( "curl http://$mrtg_ip:$mrtg_port/?ssl" );
}
else {
echo "实时状态查看功能未开启";
}
break;
}
default:
WEBUI_alert("无效参数:service_action=".$_GET['service_action']);
}
?>


注意这里的参数$service_path = $_GET['service_path'];
最后$service_path进入函数start_service,stop_service,status_service
这些函数的定义在文件/kssl/kssl/WEBUI/www/ssl/service_helper.php,跟进

function start_service( $service_path )
{
global $SSL_DIR;
global $PMONITOR_DIR;
/* 先检查HRP的配置文件,再运行PMonitor,错误定义见hrp-can-start.sh脚本的注释 */
exec( "$SSL_DIR/bin/hrp-can-start.sh /kssl/HRP/cfg/$service_path 2>&1", $results, $ret );
switch( $ret ) {
case 0:
WEBUI_exec( "$PMONITOR_DIR/bin/PMonitor --run -f $SSL_DIR/cfg/".$service_path."/PMonitor.conf > /dev/null", true );
return true;
case 1:
WEBUI_alert( "配置文件不存在,不能启动服务:" );
return false;
case 2:
WEBUI_alert( "本机现在处于双机热备的待机状态,不能启动服务" );
return false;
case 3:
WEBUI_alert( "使用了网络配置中不存在的IP地址,不能启动服务" );
return false;
case 4:
WEBUI_alert( "监听的端口已经被其他程序所使用,不能启动服务" );
return false;
case 5:
WEBUI_exec( "$SSL_DIR/bin/hrp-can-start.sh /kssl/HRP/cfg/$service_path 2>&1", true, "配置文件不完整,或者进行了错误配置" );
return false;
}
}
function stop_service( $service_path )
{
global $SSL_DIR;
global $PMONITOR_DIR;
WEBUI_exec( "$PMONITOR_DIR/bin/PMonitor --kill -f $SSL_DIR/cfg/".$service_path."/PMonitor.conf", true );
}
function status_service( $service_path )
{
global $PMONITOR_DIR;
exec( "$PMONITOR_DIR/bin/PMonitor -l | grep \"HRP_$service_path \" | awk -F= '{print $3}'", $results, $ret );
switch( $results[0] ) {
case "":
return "已停止";
case "NORMAL":
return "已启动";
case "INIT":
case "RETRYING":
return "启动中";
}
}


由于status_service函数中命令有双引号保护,双引号被转义,导致利用失败
看看start_service和stop_service函数,$service_path进入了WEBUI_exec,跟进此函数
文件/kssl/kssl/WEBUI/www/global/common.php

function WEBUI_exec( $cmd, $show_err = false, $note = '' )
{
exec( $cmd, $results, $ret );
if( $ret != 0 ) {

if( $show_err ) {
$err = '执行 '.$cmd.' 命令失败';
foreach ( $results as $err_line ) {
//2007-5-8 yanhm bugfix for 0005289:错误信息太多 +{{
//错误信息中删去命令帮助信息
if ( strncmp($err_line, "Usage", 5) != 0 )
break;
//}}
$err = $err.', '.$err_line;
}

WEBUI_alert( "$note:$err" );
}

return false;
}
return true;
}


最终进入了exec中,导致命令执行
所以当service_action=start和service_action=stop时存在两处命令执行漏洞
第三处和第四处命令执行
文件/kssl/kssl/WEBUI/www/PrivManager.php

<?php 
$setup_ini = "/kssl/WEBUI/www/global/setup.ini";
$title_php = "/kssl/WEBUI/www/global/title.php";
$version_php = "/kssl/WEBUI/www/global/version.php";
$copyright_php = "/kssl/WEBUI/www/global/copyright.php";
$logo_php = "/kssl/WEBUI/www/global/logo.php";

$logo_jpg_file = "/kssl/WEBUI/www/image/logo.jpg";

function get_setup_ini()
{
global $setup_ini;
$ini_array = array();
$ini_array = parse_ini_file($setup_ini, true);

return $ini_array;
}

$setup = array();
$setup = get_setup_ini();

switch( $_POST['submit_action'] )
{
case 'set_privmanager': {

$MODE = $_POST["modes"];

$titlenames = $_POST["titlenames"];
$copyright = $_POST["copyright"];
$version = $_POST["version"];

if( $titlenames == -1 ) {
$titlenames = $_POST["othername"];
}
if( $copyright == -1 ) {
$copyright =$_POST["othercopyright"];
}
if( $version == -1 ) {
$version = $_POST["otherversion"];
}

$logo = $titlenames."入口";

$f = fopen( "$title_php", "w" );
fwrite( $f, "<?php\n" );
fwrite( $f, "echo \"$titlenames\";\n" );
fwrite( $f, "?>\n" );
fclose( $f );

$f = fopen( "$logo_php", "w" );
fwrite( $f, "<?php\n" );
fwrite( $f, "echo \"$logo\";\n" );
fwrite( $f, "?>\n" );
fclose( $f );
$f = fopen( "$copyright_php", "w" );
fwrite( $f, "<?php\n" );
fwrite( $f, "echo \"$copyright\";\n" );
fwrite( $f, "?>\n" );
fclose( $f );


$tmp_arr = explode(".", $version);
$big_version = $tmp_arr[0];
$tmp = strstr($version, ".");
$l_version = substr($tmp, 1);

$f = fopen( "$version_php", "w" );
fwrite( $f, "<?php\n" );
fwrite( $f, "\$major=\"$big_version\";\n" );
fwrite( $f, "\$minor=\"$l_version\";\n" );
fwrite( $f, "\$baseline=\"R\";\n" );
fwrite( $f, "\$build=\"110106\";\n" );
fwrite( $f, "echo \"版本:\$major.\$minor \$baseline-\$build\";\n" );
fwrite( $f, "?>\n" );
fclose( $f );

$logo_jpg = $_POST["logo_jpg"];

$logo_jpg_path = "/kssl/WEBUI/www/image/".$logo_jpg;

exec("cp --reply=yes $logo_jpg_path $logo_jpg_file");

if( $MODE == 0 ) {
$MODE_NAME = "单权模式";
} else {
$MODE_NAME = "三权模式";
}
if( WEBUI_exec( "/kssl/WEBUI/bin/PrivManager.sh $MODE", true ) ) {
WEBUI_log( LOG_INFO, "设备部署-设置完成!" );
echo "<script langauge=javascript>alert('设备部署-设置完成!')</script>";
exec("touch /kssl/WEBUI/cfg/PrivManager.conf");
echo "<script langauge=javascript>window.location='index.php';</script>";
} else {
WEBUI_log( LOG_ERR, "设备部署-设置失败!" );
echo "<script langauge=javascript>alert('设备部署-设置失败!')</script>";
}

break;
}
}
?>


可以清楚看到:
$logo_jpg = $_POST["logo_jpg"];
exec("cp --reply=yes $logo_jpg_path $logo_jpg_file");
logo_jpg存在命令执行
还有一处在:
$MODE = $_POST["modes"];
WEBUI_exec( "/kssl/WEBUI/bin/PrivManager.sh $MODE", true )
WEBUI_exec的内容见上面的内容
==============================================================================
上面的几处是直接没有判断登录状态的,直接利用,下面设计大面积登录验证绕过命令执行
首先全局文件中,文件名为*_i.php的文件为正式功能文件
且都有登录验证:

<?php	
session_start();
include_once "../global/common.php";
if( WEBUI_HasLogin() == false ) {
echo "<script language=\"javascript\">window.location = \"/login.php\";</script>";
}
?>
/* 判断是否登录 */
function WEBUI_HasLogin( $bRedirect = true )
{
if( isset($_SESSION['KLSSL_WEBUI_LOGINSTATE']) ) {
return true;
}
else {
if( $_COOKIE['KLSSL_WEBUI_USERNAME'] != "" and $_COOKIE['KLSSL_WEBUI_PASSWORD'] != "" ) {
WEBUI_log( LOG_INFO, "以Cookie方式登录" );
return WEBUI_Login($_COOKIE['KLSSL_WEBUI_USERNAME'], $_COOKIE['KLSSL_WEBUI_PASSWORD']);
}
else {
if ($bRedirect) {
header("Location: /login.php");
exit();
}
return false;
}
}
}


当KLSSL_WEBUI_USERNAME和KLSSL_WEBUI_PASSWORD不为空时
进入WEBUI_Login中
function WEBUI_Login( $username, $password )
{
unset($_SESSION['KLSSL_WEBUI_LOGINSTATE']);
if ( WEBUI_CheckPassword( $username, $password ) == false ) {
WEBUI_log( LOG_INFO, "用户".$username."登录失败" );
return false;
}
else {
WEBUI_log( LOG_INFO, "用户".$username."登录成功" );
}

$_SESSION['KLSSL_WEBUI_LOGINSTATE'] = $username;
return true;
}
最后进入WEBUI_CheckPassword中验证:
function WEBUI_CheckPassword( $username, $password )
{
global $WEBUI_DIR;

exec( "grep '$username:' $WEBUI_DIR/cfg/passwd | awk -F: '{print $2}'", $results, $ret );
if( $ret == 0 ) {
// return ( md5($password) == $results[0] ) ;
return ( $password == $results[0] ) ;
}
else {
return false;
}
}
可见名用户么密码都存在文件/kssl/kssl/WEBUI/cfg/passwd中
所以通过上面的命令执行即可读取这里的账户信息进行登录
或者再登录处提交username=12312312,随意字符串,然后password为空即可绕过登录
成功登录后台</code>
在登录后既有太对命令执行了,几乎所有的*_i.php文件都存在命令执行漏洞
如:

net_curl_i.php
<?php
$results = array();
/* 处理POST请求 */
switch( $_POST['submit_action'] ) {
case 'curl': {
$URL = $_POST['URL'];
exec( "curl -k -v --connect-timeout 30 ".$URL." 2>&1", $results, $ret );
break;
}
}
?>
net_ping_i.php
<?php
/* 处理POST请求 */
switch( $_POST['submit_action'] ) {
case 'ping': {
$IP = $_POST['IP'];
exec( "ping -W 3 -c 4 ".$IP, $results, $ret );
break;
}
}
?>
net_telnet_i.php
<?php
$results = array();
/* 处理POST请求 */
switch( $_POST['submit_action'] ) {
case 'telnet': {
$IP = $_POST['IP'];
$PORT = $_POST['PORT'];
//exec( "/kssl/PMonitor/bin/Connector -p TCP -i ".$IP." -o ".$PORT, $results, $ret );
exec( "/bin/nc -z ".$IP." ".$PORT, $results, $ret );
//WEBUI_alert("/kssl/PMonitor/bin/Connector -p TCP -i ".$IP." -o ".$PORT);
break;
}
}
?>
net_snmp_i.php
$v3username=$_POST['v3username'];
$password=$_POST['password'];
$enc_password=$_POST['enc_password'];

$snmpconf = get_snmpconf();
$snmpconf['listenaddr'] = $ListenAddr;
$snmpconf['version'] = $version;
$snmpconf['v3username'] = $v3username;
$snmpconf['password'] = $password;
$snmpconf['enc_password'] = $enc_password;

exec("net-snmp-config --create-snmpv3-user -ro -a $password -x $enc_password -X DES -A MD5 $v3username");
剩下的就不一一列举了

漏洞证明:

1.png


2.png


可以看到id成功执行,且为root权限

3.png


4.png


成功写入文件

修复方案:

登录验证,执行命令拼接参数过滤

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:15

确认时间:2016-01-19 11:52

厂商回复:

CNVD未直接复现所述情况,已经由CNVD通过以往建立的处置渠道软件生产厂商通报,由其后续提供解决方案并协调相关用户单位处置。

最新状态:

暂无