Tomcat PUT方法任意写文件漏洞复现(CVE-2017-12615)
2020-12-25 21:47:14 Author: www.freebuf.com(查看原文) 阅读量:117 收藏

Tomcat测试版本:7.0.79

漏洞影响全部的 Tomcat 版本:Apache Tomcat 7.0.0 - 7.0.79 (windows环境)

在tomcat官网下载一个Tomcat7的源码压缩包,打开来慢慢看看里面的.xml文件java代码

关键配置

配置文件路径

apache-tomcat-7.0.10-src\conf\web.xml

DefaultServlet类处理除了JSP文件以外的静态资源

<!-- The default servlet for all web applications, that serves static     -->
<!-- resources.  It processes all requests that are not mapped to other   -->
<!-- servlets with servlet mappings (defined either here or in your own   -->
<!-- web.xml file).  This servlet supports the following initialization   -->
<!-- parameters (default values are in square brackets):                  -->

readonly默认为true,在(CVE-2017-12615)的情况下为false

<!--   readonly            Is this context "read only", so HTTP           -->
<!--                       commands like PUT and DELETE are               -->
<!--                       rejected?  [true]                              -->

servlet-name:default对应的是servlet-class:DefaultServlet

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
		<init-param>
        <param-name>readonly</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

JspServlet类负责JSP文件的汇编与运行

<!-- The JSP page compiler and execution servlet, which is the mechanism  -->
<!-- used by Tomcat to support JSP pages.  Traditionally, this servlet    -->
<!-- is mapped to the URL pattern "*.jsp".  This servlet supports the     -->
<!-- following initialization parameters (default values are in square    -->
<!-- brackets):                                                           -->

servlet-name:jsp对应的是servlet-class:JspServlet

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

servlet-mapping主要是截获请求的:

    1. 如果url-pattern定义的是路径,那么以后所有对这个路径下资源的请求都会由servlet-name中定义的servlet处理;
    2. 如果你的url-pattern定义的是资源格式例如*.jsp等,那么对于所有符合这种格式的资源的请求都由指定的servlet处理。

这里说明了/类型的资源都由servlet-name:default处理,*.jsp*.jspx类型的资源都由servlet-name:jsp处理

<!-- ================ Built In Servlet Mappings ========================= -->

<!-- The servlet mappings for the built in servlets defined above.  Note  -->
<!-- that, by default, the CGI and SSI servlets are *not* mapped.  You    -->
<!-- must uncomment these mappings (or add them to your application's own -->
<!-- web.xml deployment descriptor) to enable these services              -->

  <!-- The mapping for the default servlet -->
  <servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!-- The mappings for the JSP servlet -->
  <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jsp</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jspx</url-pattern>
  </servlet-mapping>

代码分析

DefaultServlet.java文件路径

apache-tomcat-7.0.10-src\\java\\org.apache\\catalina\\servlets\\DefaultServlet.java

受影响的doPut函数

/**
 * Process a PUT request for the specified resource.
 *
 * @param req The servlet request we are processing
 * @param resp The servlet response we are creating
 *
 * @exception IOException if an input/output error occurs
 * @exception ServletException if a servlet-specified error occurs
 */
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

    if (readOnly) {
        resp.sendError(HttpServletResponse.SC_FORBIDDEN);
        return;
    }

    String path = getRelativePath(req);

    boolean exists = true;
    try {
        resources.lookup(path);
    } catch (NamingException e) {
        exists = false;
    }

    boolean result = true;

    // Temp. content file used to support partial PUT
    File contentFile = null;

    Range range = parseContentRange(req, resp);

    InputStream resourceInputStream = null;

    // Append data specified in ranges to existing content for this
    // resource - create a temp. file on the local filesystem to
    // perform this operation
    // Assume just one range is specified for now
    if (range != null) {
        contentFile = executePartialPut(req, range, path);
        resourceInputStream = new FileInputStream(contentFile);
    } else {
        resourceInputStream = req.getInputStream();
    }

    try {
        Resource newResource = new Resource(resourceInputStream);
        // FIXME: Add attributes
        if (exists) {
            resources.rebind(path, newResource);
        } else {
            resources.bind(path, newResource);
        }
    } catch(NamingException e) {
        result = false;
    }

    if (result) {
        if (exists) {
            resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
        } else {
            resp.setStatus(HttpServletResponse.SC_CREATED);
        }
    } else {
        resp.sendError(HttpServletResponse.SC_CONFLICT);
    }

}

动态调试

进入doPut函数,上传的文件名为12w3.jsp/,可以看到readOnly==false

因为12w3.jsp/不存在服务器中,所以调用bind函数进行写入。进入resources.bind函数

进入dirContext.bind函数

1608899327_5fe5daff7dbdb708d4034.png!small?1608899326982

进入bind函数

1608899356_5fe5db1ce7c2f04d496ff.png!small?1608899356500

传入的name参数为/12w3.jsp/,在File函数中会将name中的/去除,进入File函数进行查看

1608899374_5fe5db2ee47a591f7dcb8.png!small?1608899374467

child==/12w3.jsp/,由fs.normalize函数处理,进入fs.normalize函数

1608899458_5fe5db823abf801db1856.png!small?1608899457822

child==/12w3.jsp/,由fs.normalize函数处理,进入fs.normalize函数

1608899474_5fe5db9219e78cc2a65a2.png!small?1608899473737

normalize(path, len, off)函数对path处理完后,返回\12w3.jsp

1608899492_5fe5dba444bcd8720547c.png!small?1608899491812

File函数中出来,把name==/12w3.jsp/传进rebind函数,进入rebind函数

1608899596_5fe5dc0ce96d744cdfcbb.png!small?1608899596500

rebind函数中调用File函数获得完整路径F:\\Tomcat\\apache-tomcat-7.0.79\\webapps\\ROOT\\12w3.jsp

1608899616_5fe5dc20060a4c5a264f3.png!small

将文件内容写入F:\\Tomcat\\apache-tomcat-7.0.79\\webapps\\ROOT\\12w3.jsp文件

1608899696_5fe5dc7087f089c3c5b41.png!small?1608899696106

上传完成。

访问Tomcat主页,抓包

1608899855_5fe5dd0fa91b68c201170.png!small?1608899856461

右击,选择send to repeater

1608899876_5fe5dd24d84e0f0303962.png!small?1608899876380

修改红框中的内容:

  1. GET请求改为PUT
  2. 添加文件名backdoor.jsp/(注意后面要添加/
  3. 添加文件内容

1608900007_5fe5dda7491a1bcb9125d.png!small?1608900006933

1608900013_5fe5ddadb1bda7c2c1069.png!small?1608900013230

点击send发送。查看response,文件创建成功。

1608900134_5fe5de2685a30ba14e816.png!small?1608900134310

查看Tomcat根目录,多了一个backdoor.jsp,上传成功。

1608900147_5fe5de339b725ec089d64.png!small?1608900147241

配置Tomcat7:https://www.freebuf.com/articles/others-articles/258885.html

IDEA远程调试Tomcat:https://www.freebuf.com/sectool/258891.html

利用PUT方法的多种方法:https://wh0ale.github.io/2018/12/23/2018-12-23-利用PUT方法的多种方法/

servlet-mapping干什么用的:https://zhidao.baidu.com/question/538577773.html

seebug:https://paper.seebug.org/399/


文章来源: https://www.freebuf.com/articles/web/258907.html
如有侵权请联系:admin#unsafe.sh