代碼審計之seacms v6.45 前臺Getshell 復現分析

1.環境:php

php5.5.38+apache+seacms v6.45css

seacms目錄結構:web

│─admin //後臺管理目錄
│ │─coplugins //已停用目錄
│ │─ebak //帝國備份王數據備份
│ │─editor //編輯器
│ │─img //後臺靜態文件
│ │─js //後臺js文件
│ │─templets //後臺模板文件
│─article //文章內容頁
│─articlelist //文章列表頁
│─comment //評論
│ │─api //評論接口文件
│ │─images //評論靜態文件
│ │─js //評論js文件
│─data //配置數據及緩存文件
│ │─admin //後臺配置保存
│ │─cache //緩存
│ │─mark //水印
│ │─sessions //sessions文件
│─detail //視頻內容頁
│─include //核心文件
│ │─crons //定時任務配置
│ │─data //靜態文件
│ │─inc //擴展文件
│ │─webscan //360安全監測模塊
│─install //安裝模塊
│ │─images //安裝模塊靜態文件
│ │─templates //安裝模塊模板
│─js //js文件
│ │─ads //默認廣告目錄
│ │─player //播放器目錄
│─list //視頻列表頁
│─news //文章首頁
│─pic //靜態文件
│ │─faces //表情圖像
│ │─member //會員模塊界面
│ │─slide //舊版Flash幻燈片
│ │─zt //專題靜態文件
│─templets //模板目錄
│─topic //專題內容頁
│─topiclist //專題列表頁
│─uploads //上傳文件目錄
│─video //視頻播放頁
│─weixin //微信接口目錄
└─index.php //首頁文件

2.利用代碼正則表達式

poc1apache

http://seacms.test/search.php
POST:
searchtype=5&order=}{end if} {if:1)phpinfo();if(1}{end if}

poc2:api

POST:
searchtype=5&order=}{end if}{if:1)$_POST[func]($_POST[cmd]);//}{end if}&func=system&cmd=whoami
searchtype=5&order=}{end if}{if:1)$_POST[func]($_POST[cmd]);if(1}{end if}&func=system&cmd=whoami

3.執行效果
緩存

4.漏洞分析安全

 

漏洞產生鏈如上圖所示,在search.php的212行下斷點,由於在此處產生了parseIf()函數的調用,而且最終的命令執行是發生在此函數中,用payload打一次,將停在此處,進入此函數進行分析微信

如上圖所示,其中buildregx函數是構建php的原生正則表達式session

接下來使用$labelRule規則進行preg_match_all匹配出了全部知足的結果,並放在$iar中,我猜想這裏class頂一個兩個css樣式,經過if條件來調整按鈕樣式的

經過這4行代碼將$iar中的每條記錄分爲條件,以及條件體

接着判斷正則$labelRule2所表示的字符串是否包含於條件體中,默認是不包含的,而且$labelRule3中包含的{else}字符串是出如今條件體中的,因此進入循環,此時將條件體又分爲兩塊,

分別表明兩種不一樣的css樣式,接着就是觸發漏洞的核心,在這裏也發現了eval函數的調用,用於代碼執行的經典函數

如上圖所示,將$strif變量與if條件進行了拼接,那麼此處是否存在代碼注入的狀況?的確如此,此時能夠看看$strif的值

其中第95條就包含有咱們的payload,那麼此時將payload和if條件進行拼接能夠獲得:

if(1)phpinfo();if(1)

此時成功閉合了php語句,而且跟後面的$ifFlag條件體也成功閉合了,因此可以成功進行代碼執行!!!代碼注入真刺激~,到此已經實現RCE,那麼想一想爲啥會形成這樣的漏洞,我向上看看變量是如何傳遞過來的,

 

parse函數就是在main.class.php這個類文件中定義的處理if代碼塊的函數,其入口參數爲$content,那麼回到調用parseIf函數的地方,也就是search.php,由於咱們漏洞文件也在該文件,那麼咱們POST傳遞過來的payload最終會傳遞到content而後再進入到parseIf函數進行處理,而該處調用又是存在於echoPageSearch()函數中,那麼回到該函數入口處,在其上方發現了對其的調用,如今在此文件全局搜索如下POST字符串

如上圖所示,沒有搜索到POST,那麼有可能包含在common.php中,進去看看

正與咱們所猜想的同樣,此時能夠在註釋中發現,其經過一段循環將POST中的值註冊爲變量了,而且咱們提交的標量裏不能包含cfg_和GLOVALS字符串,而且在COOKIE中不可以設置,這裏是爲了防止變量覆蓋

接下來還調用了_RunMagicQuotes()函數對變量值進行過濾,實際上進行了一個addslashes()函數的操做,並不影響咱們實際所用的payload

能夠從上圖看到傳遞進來的變量直接註冊爲內部的變量了,而且變量內容沒有發生變化,那麼說明都是咱們能夠控制的,由於最後parse處理的變量是$content,那麼咱們須要弄清$order是如何賦值到$content中的,

再次文件中搜索$order的使用

能夠找到4處調用,第一處是在函數內部global來引用,第二處又將$order賦值給$orderStr,可是這樣賦值沒有影響到$content變量,所以看最後一處調用

如上圖所示,將$content中的{searchpage:ordername}部分替換爲了$order變量的值,爲了更清楚的看看$content的內容是如何變化的,咱們能夠在158行和160行處下斷點,從新執行一次payload,此時能夠在此斷點處查看$content的值,並將替換先後的$content值進行對比,能夠看到str_replace()函數將進行3處替換

即在此處完成了payload對$content變量的注入,以後在最終調用parseIf()函數處理$content變量以前,又對$content的內容進行了屢次替換,可是並無影響到咱們的payload,接下來看看程序是如何解析出來咱們的paylaod的

上面已經說過程序是利用

{if:(.*?)}(.*?){end if}

這一串正則來對$content變量進行匹配的,那麼此時對於咱們注入payload的部分,最終是eval()中包含$strIf變量,那麼因爲是貪婪匹配,因此首先將會匹配到第一部分{if:"}{end if} 將「匹配出來做爲一部分,而後下一次匹配再匹配到1)phpinfo();if(1做爲另外一次匹配的部分

能夠從$iar變量的值中驗證咱們的推理是正確的,這裏存在3處相同的匹配是由於以前$order對$content進行了3次payload注入,這樣的構造的確很巧妙,首先1)phpinfo();if(1這一部分要閉合後面eval部分,而後再要知足前面正則匹配的邏輯可以把payload完整匹配出來,

這可能也是開發人員沒有想到的,以後拼接的方法上面已經講了,漏洞的整個分析流程到此結束。

5.修復方法:

在64行添加

$order = ($order == "commend" || $order == "time" || $order == "hit") ? $order : "";

相關文章
相關標籤/搜索