CVE-2022-28219 is an unauthenticated remote code execution vulnerability affecting Zoho ManageEngine ADAudit Plus, a compliance tool used by enterprises to monitor changes to Active Directory. The vulnerability comprises several issues: untrusted Java deserialization, path traversal, and a blind XML External Entities (XXE) injection. This is a vulnerability that NodeZero, our autonomous pentesting product, has exploited to not only execute code remotely, but in some cases compromise domain administrator accounts. If you’re running ADAudit Plus in your enterprise, we strongly recommend upgrading to build 7060 or later to fix this vulnerability.
Discovery
We regularly encounter ManageEngine products in internal pentests. The products related to Active Directory management (ADManager Plus, ADSelfService Plus, ADAudit Plus, etc) are especially prevalent. These applications are also attractive to attackers because of the privileged access they have to Active Directory. We decided to take a closer look at ADAudit Plus to see what we could find.
Potential RCE Vector: Return of Cewolf
Typically, in a white box source code review, we begin by understanding what backend API endpoints are accessible to an unauthenticated attacker. For Java web applications, the web.xml file is the place to start.
One of the first things that stood out, and we were surprised to see, was the presence of a /cewolf endpoint handled by the CewolfRenderer servlet in the third-party Cewolf charting library. This is the same vulnerable endpoint from CVE-2020-10189, reported by @steventseeley against ManageEngine Desktop Central. The FileStorage class in this library was abused for remote code execution via untrusted Java deserialization.
Inspecting the library further, we found that, in addition to deserializing untrusted code, the library doesn’t sanitize input file paths. Using the img parameter, we could deserialize a Java payload anywhere on disk.
Assuming there was already a file on disk containing a Java payload, we could trigger deserialization and command execution with a request like this:
Note the servlet request path needs to end in an image file extension like .png to bypass a security filter.
Finding an XXE
We had a powerful remote code execution primitive in hand and needed to find a way to upload a Java payload anywhere on disk. We found several ways for unauthenticated users to upload files but initially had difficulty uploading an arbitrary file containing a Java payload because of security filters and file type checks.
One of the features of ADAudit Plus is the ability to collect security events from agents running on other machines in the domain. To our surprise, we found that a few of the endpoints that agents use to upload events to ADAudit Plus were unauthenticated. This gave us a large attack surface to work with because there’s a lot of business logic that was written to process these events. While looking for a file upload vector, we found a path to trigger a blind XXE vulnerability in the ProcessTrackingListener class, which handles events containing Windows scheduled task XML content. This class was using the dangerous default version of Java’s DocumentBuilderFactory class, which permits external entity resolution and is vulnerable to XXE injection.
We found a request of the following form could trigger the XXE:
curl -H 'Content-Type: application/json' -X POST http://<adap_ip>:<port>/api/agent/tabs/agentData -d @payload.json
The only pre-requisite that an attacker needs to know ahead of time is the name of the fully qualified Windows domain that the ADAudit Plus application is monitoring. This is trivial for attackers to discover.
Blind XXE vulnerabilities in Java can be hard to exploit, but in this case we were aided by the old Java runtime bundled with ADAudit Plus. By default ADAudit Plus ships with Java 8u051.
With the old Java runtime, we found the blind XXE can be used to do all of the following:
exfiltrate files over FTP
get directory listings over FTP
upload files!!
In the wild we’ve found that about 3/4 of the vulnerable ADAudit Plus installs are using the old runtime. We found Java runtime versions 8u131 and later have protections in place to prevent the above actions.
Exploitation
XXE to RCE
In a test environment, we set up ADAudit Plus on host 10.0.220.100, running under domain user a-jsmith. Our attacker IP was 10.0.220.200. Upon install, ADAudit Plus automatically detected that it was part of the SMOKE.NET domain.
Step 1: Generate a Java payload using the CommonBeanutils1 gadget. For instance, using ysoserial to run calc.exe:
Step 2: Use the XXE vulnerability to upload this payload. There is a really nice Java-specific XXE technique disclosed by Timothy Morgan in 2013 to upload a file using the jar file protocol and a “blocking” server that doesn’t close the connection after the upload. This file is uploaded to a temp folder with a randomly generated name.
The BlockingServer serves the file and keeps the connection open so the temp file is not deleted.
Step 3: Use the XXE vulnerability to locate the file path of the uploaded payload. We used the XXE FTP server from the GitHub project here to exfiltrate directory listings to find the payload:
python2 xxe-ftp-server.py 10.0.220.200 3000 2122
Port 3000 hosts an external DTD, and port 2122 is the FTP Server port.
Then send the following request:
curl -H 'Content-Type: application/json' -X POST http://10.0.220.100:8081/api/agent/tabs/agentData -d @payload_list.json
The XXE FTP server serves the DTD over HTTP and receives the contents of the temp directory over FTP. In this example, it can be seen that the file was uploaded to the path c:/users/a-jsmith/appdata/local/temp/jar_cache7858836562026605742.tmp.
Step 4: Use the /cewolf endpoint to deserialize the contents of the uploaded file and trigger the execution of the command:
We automated these steps in a self-contained PoC script on GitHub here.
XXE to SSRF to NTLM Relay
As a side note, regardless of the Java runtime version, XXE vulnerabilities in Java and on Windows can also be used to capture and relay the NTLM hashes of the user account under which the application is running. This is because the Java HTTP client will attempt to authenticate over NTLM if it connects to a server requiring NTLM to authenticate.
This is especially useful for an attacker if the ADAudit Plus application is running under a privileged account. As an example, we run the well-known responder tool on the attacker machine:
Responder captures the NTLMv2 hash of the a-jsmith user under whom the ADAudit Plus application is running.
These hashes can cracked by attackers to recover the plaintext password, or they can be relayed to targets directly and used to execute code on those targets, using well-known tools such as ntlmrelayx from the impacket toolkit.
Post-Exploitation
Applications that integrate with Active Directory have to store credentials to connect to it. In the case of ADAudit Plus, these credentials are stored encrypted in its database. It’s possible to reverse the encryption to access these credentials in the clear.
In the wild, we’ve found that these credentials are often highly privileged. ADAudit Plus makes it easy for users to get started with domain admin credentials, and we’ve seen users take this easy path rather than setting up a dedicated service account with restricted privileges. When this happens, NodeZero will fully compromise the domain through ADAudit Plus, generating an attack graph that looks like this.
Disclosure Timeline
March 28, 2022: Vulnerability disclosed to Zoho via bug bounty program
March 28, 2022: Vulnerability confirmed by Zoho
March 30, 2022: New build ADAudit Plus 7060 released by Zoho
April 5, 2022: CVE-2022-28219 published
April 5, 2022: Detection and exploitation integrated into Horizon3 NodeZero pentest operations
June 29, 2022: This detailed disclosure
The patch in ADAudit Plus 7060 fixes the vulnerability by:
Removing the /cewolf endpoint altogether
Using a secure version of DocumentBuilderFactoryin the ProcessingTrackingListener class
Requiring authentication in the form of an agent GUID between agents and ADAudit Plus
Thanks to Zoho for prompt handling of this vulnerability. We highly recommend users update to ADAudit Plus build 7060 or later, and ensure ADAudit Plus is configured with a dedicated service account with restricted privileges.
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
Cookie
Duration
Description
__cfruid
session
Cloudflare sets this cookie to identify trusted web traffic.
_GRECAPTCHA
5 months 27 days
This cookie is set by the Google recaptcha service to identify bots to protect the website against malicious spam attacks.
cookielawinfo-checkbox-advertisement
1 year
Set by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Advertisement" category .
cookielawinfo-checkbox-analytics
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional
11 months
The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
CookieLawInfoConsent
1 year
Records the default button state of the corresponding category & the status of CCPA. It works only in coordination with the primary cookie.
OptanonConsent
1 year
OneTrust sets this cookie to store details about the site's cookie category and check whether visitors have given or withdrawn consent from the use of each category.
viewed_cookie_policy
11 months
The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Cookie
Duration
Description
AnalyticsSyncHistory
1 month
LinkedIn - Used to store information about the time a sync took place with the lms_analytics cookie
bcookie
2 years
LinkedIn sets this cookie from LinkedIn share buttons and ad tags to recognize browser ID.
bscookie
2 years
LinkedIn sets this cookie to store performed actions on the website.
lang
session
LinkedIn sets this cookie to remember a user's language setting.
li_gc
2 years
LInkedIn Used to store consent of guests regarding the use of cookies for non-essential purposes
lidc
1 day
LinkedIn sets the lidc cookie to facilitate data center selection.
UserMatchHistory
1 month
LinkedIn sets this cookie for LinkedIn Ads ID syncing.
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Cookie
Duration
Description
_calendly_session
21 days
Calendly, a Meeting Schedulers, sets this cookie to allow the meeting scheduler to function within the website and to add events into the visitor’s calendar.
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Cookie
Duration
Description
_ga
2 years
The _ga cookie, installed by Google Analytics, calculates visitor, session and campaign data and also keeps track of site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognize unique visitors.
_ga_V462VSRXXS
2 years
This cookie is installed by Google Analytics.
6suuid
2 years
6sense is a B2B predictive intelligence engine for marketing and sales.
CONSENT
2 years
YouTube sets this cookie via embedded youtube-videos and registers anonymous statistical data.
pardot
past
The pardot cookie is set while the visitor is logged in as a Pardot user. The cookie indicates an active session and is not used for tracking.
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
Cookie
Duration
Description
VISITOR_INFO1_LIVE
5 months 27 days
A cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface.
YSC
session
YSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages.
yt.innertube::nextId
never
This cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen.
yt.innertube::requests
never
This cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen.