記一次對wuzhicms的審計
前言
在網上漫游發現的一個cms cnvd也是有提交的
也是初次審計這種
官網:https://www.wuzhicms.com/
現在也已經好像沒更新了
也是先看了一會代碼 才知道這是MVC的 之前由于也沒有了解過MVC 就很懵
開始啥都沒看懂
后來經過百度 和 求助了一波團隊的時候 總算是有一點點明白了 能把代碼走動
MVC全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典范,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,將業務邏輯聚集到一個部件里面,在改進和個性化定制界面及用戶交互的同時,不需要重新編寫業務邏輯。MVC被獨特的發展起來用于映射傳統的輸入、處理和輸出功能在一個邏輯的圖形化用戶界面的結構中。(百度的)
而在之后的路徑中會看見m f v這幾個參數
m就是文件夾 f就是文件 v就是方法
就先大概介紹這些
下面開始審計
sql注入肯定是容易找的 就先找sql注入了
工具:seay phpstorm phpstudy
第一處 sql注入(可惜是后臺的)
先直接全局搜索select

這個函數 展示沒有發現有過濾
然后找哪里調用了這個函數

通過全局搜索 在這個地方 發現調用了這個函數
然后查看傳遞的參數
主要傳遞的是55行這個$where參數 傳到了函數
然后我們看$where參數的組成 里面有兩個變量$siteid $keywords
我們是可能可控的
先看$keywords 因為這個沒調用函數 但是調用了一個$GLOBALS來獲取值
這里就又要介紹下$GLOBALS 因為剛開始我也沒懂這個是怎么用來獲取值的 知道我百度一陣之后
和代碼翻翻之后 在一個文件中發現了

這個cms用$GLOBALS來獲取全部的變量 直接把GET POST代替了
具體怎么代替的就不跟進解釋了 一句話 就是$GLOBALS可以取到get post的傳的值
那么這個keywords 前面又沒有定義變量啥的 大概率是 傳參的 (后面經過驗證 也的確如此)
那另一個參數就不用看了 就先控制這個參數才進行注入了
有了可控參數 現在就需要找到整個payload了
可能熟悉MVC的師傅 就知道該怎么構造payload了 但我沒學過MVC 也不了解 就只能用其他方式來找了

在這個文件看見了廣告管理的注釋
那我就去后臺找這個功能了

又因為調用的函數是search嘛 那就是這個搜索框 八九不離十了
直接輸入1 然后搜索 但是這樣看不出來
就輸入1然后抓包 看看包了

好了 這樣路徑參數什么的也出來了
就全部復制到url
然后構造
payload :SELECT COUNT(*) AS num FROMwz_promote_placeWHEREsiteid=’1’ ANDnameLIKE ‘%1%’ or extractvalue(1,concat(0x7e,(select database())))%23
閉合%‘%23
這里我嘗試了下 盲注 和報錯都是可以的

如果現在到過頭來看 就一個簡單的搜索框的注入沒什么花里胡哨的 過濾也沒 但審計來看 還是繞了一大圈子
第二處 前臺sql注入
還是在搜索select的時候 發現在mysql.class文件下有一個函數里面有select 并且后面的拼接也沒有任何的過濾

然后我們搜索哪里調用了這個函數
首先是在api目錄下的sms_check文件中發現調用了get_one函數 并且參數是通過前面的$code拼接

我們可以看到code 先是通過$GLOBALS來獲取參數param的值 從前面的介紹可以知道 $GLOBALS是可以獲取post get的值 這個文件前面沒有定義param變量 那么 這個param應該就是post 或者get 就是我們可控的 這也是導致注入的點

code還通過strip_tags() 函數 而這個函數的作用是剝去html標簽 應該是過濾xss吧大概
之后就直接傳入了函數 繼續更進函數 因為這個文件前面還引入了db類

這個函數應該是調用的這個文件里面的
來到這個文件

可以看到這個get-one函數里面 還調用了一個array2sql函數來處理$where
那先來看看這個函數的作用

可以看到這個函數是用來過濾的
如果是數組 這進入if 把括號 單引號這些過濾掉
不是則走else 過濾 %20 %27
然后返回參數
但也就是這個過濾的地方 沒有防護到位
我們傳的參數不是數組 所以就沒有走if
而else里面過濾的卻是 %20 %27
我們傳參的時候盡管是經過url編碼的 但是web服務器會自動解碼一次 所以 我們傳到后端代碼處的時候是沒有進行url編碼 相當于
但是二次編碼的就不一樣了 因為web服務器只解碼一次
如果是二次編碼這里的else過濾就起效果
return 調用的get_one 則是最開始看見的mysql.class文件里面了
下面就可以開始直接構造payload了 這里通過代碼分析可以看到是單引號閉合

單引號報錯
閉合顯示正常頁面

就進行盲注
我用的報錯
payload:[http://192.168.1.7/wuzhicms/api/sms_check.php?param=1%27+or%20extractvalue(1,concat(0x7e,(select%20database())))%23](http://192.168.1.7/wuzhicms/api/sms_check.php?param=1'+or extractvalue(1,concat(0x7e,(select database())))%23)

第三處 后臺sql注入
從前面兩個分析 我發現的注入的地方就存在兩個函數中get_list get_one
然后直接全局搜索這兩個函數 看看什么地方調用

可以看到 在copyfrom.php中listing函數下調用了這個函數
然后我們網上分析 看看什么是可控的
主要傳進去的就一個$where 和 $page

可以看到page會被intval()函數 轉化為整數 所以我們不考慮它
我們看看where 在if內部 想要進入if 就需要通過GLOBALS獲取到keywords
相當于就要傳參嘛
然后在看里面 就沒有過濾這些 直接拼接
這里也可以看出 閉合方式是百分號單引號 %’

我們在來到mysql文件中定義的這個函數 也可以看到 是對where沒有過濾處理的
那么 有了前面的基礎 直接來構造
payload:http://192.168.1.7/wuzhicms/index.php?m=core&f=copyfrom&v=listing&_su=wuzhicms&keywords=%27


報錯了 直接插入報錯注入的
payload:http://192.168.1.7/wuzhicms/index.php?m=core&f=copyfrom&v=listing&_su=wuzhicms&keywords=1%%27%20or%20extractvalue(1,concat(0x7e,(select%20database())))%23

第四處 后臺任意文件刪除
通過全局搜索unlink函數 來找文件刪除

在這個文件下找到一個刪除文件的函數 然后我們繼續找哪里調用了這個函數


還是在這個文件 找到了一個del函數 里面調用了刪除文件的函數

然后來分析調用的過程 調用刪除的時候通過把$path和ATTACHMENT_ROOT 拼接
而ATTACHMENT_ROOT是前面定義的一個默認路徑

path則是前面的$url 來的
在看前面的if 如果path有值則進入到if里面 然后經過的數據庫的get_one查詢操作 應該這里是要查出一個東西
但是因為我數據庫是空的 則進入的是第一個if里面 哪怕是查出1條 也是可以的
這里也沒有其他過濾
然后網上看url的來源

GLOBALS 那就可以直接通過傳參的 前面也介紹了 id為空的話 也就進入到了else里面
到這里也就可以構造payload了
先在根目錄下創建一個文件

然后構造
http://192.168.1.7/wuzhicms/index.php?m=attachment&f=index&v=del&_su=wuzhicms&url=../1.txt

這里我把最終刪除的路徑 打印了出來

文件也是成功刪除
第五處 后臺任意文件上傳
直接搜索file_put_contents函數

在set_cache函數下發現寫入文件的函數 $data并且沒有過濾是直接通過參數傳過來的
然后全局搜索 在哪里調用了這個函數

這個set函數下調用了這個函數
并且寫入文件的內容是可控的
通過打印 知道了 寫入的路徑 文件名

并且這里也沒有過濾
直接構造payload

然后訪問文件

后面又發現一個函數調用的set_cache

過程是一樣的 基本上 就沒有演示了
這里還要注意一點 這里是寫入的緩存文件 不是一直存在的 我重啟之后 寫入的內容就還原了
應該是還有的 就沒有繼續找這個了
第六處 信息泄露
最后在后臺頁面發現一出phpinfo


一個垃圾的信息泄露
最后
肯定還有審漏的
經過這個cms的審計過后 對MVC這種框架的也有了基本的認識了 以后遇到也不至于這樣的無厘頭 不知道怎么搞路由 怎么調用的
有了一個新的開始
如果此文有什么不對點 師傅們指出 學習學習 這也是繼前面幾篇之后新的一次嘗試把 但回過頭來看這個cms 也就因為mvc 所以調用的時候不同 其他的點 漏洞的地方還是規規矩矩 大差不差的和以前的比較的話