Apache Tomcat 7.0.0 - 7.0.79
下载tomcat-7.0.50:
https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.50/bin/
修改conf/web.xml中的readonly初始化参数设置为false,然后启动即可:
0x03 漏洞复现
使用PUT方式上传文件到ROOT目录下,返回201 Created为创建成功:
如果返回的状态码是204代表存在一个同名文件,上传后会覆盖原来的文件:
0x04 设置远程调试
在tomcat-7.0.50中远程调试默认监听在0.0.0.0:8000上,使用./catalina.sh jpda run启动即可:
设置idea的远程调试即可:
0x05 漏洞分析
首先看tomcat的Servelt配置,ROOT目录的web.xml是存放到tomcat目录下的conf/web.xml里面,默认存在两个servlet一个是DefaultServlet另一个是JspServlet:
其中的DefaultServlet对进入到网站的的所有请求都有效,JspServlet对后缀名为jsp或者jspx的请求有效:
在DefaultServlet的doPut方法上下断点,然后使用burp抓包就能进入断点处,进去之后会检查配置文件中的readonly是否为真,如果为真则返回403,其调用栈如下:
doPut:510, DefaultServlet (org.apache.catalina.servlets)
service:650, HttpServlet (javax.servlet.http)
service:728, HttpServlet (javax.servlet.http)
internalDoFilter:305, ApplicationFilterChain (org.apache.catalina.core)
doFilter:210, ApplicationFilterChain (org.apache.catalina.core)
doFilter:52, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:243, ApplicationFilterChain (org.apache.catalina.core)
doFilter:210, ApplicationFilterChain (org.apache.catalina.core)
invoke:222, StandardWrapperValve (org.apache.catalina.core)
invoke:123, StandardContextValve (org.apache.catalina.core)
invoke:171, StandardHostValve (org.apache.catalina.core)
invoke:100, ErrorReportValve (org.apache.catalina.valves)
invoke:953, AccessLogValve (org.apache.catalina.valves)
invoke:118, StandardEngineValve (org.apache.catalina.core)
service:409, CoyoteAdapter (org.apache.catalina.connector)
process:1044, AbstractHttp11Processor (org.apache.coyote.http11)
process:607, AbstractProtocol$AbstractConnectionHandler (org.apache.coyote)
run:313, JIoEndpoint$SocketProcessor (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)
Readonly的值是在servlet初始化的时候从配置文件中读取出来:
检查完readonly的值之后会获取请求路径,然后资源文件中查找是否存在如下方法:
org.apache.naming.resources.ProxyDirContext#lookup(java.lang.String)方法
首先找的是缓存资源,存在的缓存资源如下图:
找不到则进入
org.apache.naming.resources.ProxyDirContext#cacheLoad方法
中进行查找:
最终无法在cache中无法找到,接下来就会抛出没有找到的异常:
也就是将doPut中的exists置为false:
紧接着会获取请求:
获取到用户的请求之后进入
org.apache.naming.resources.Resource#Resource(java.io.InputStream)中
新建的newResource对象会和获取到的路径(path)进入到
org.apache.naming.resources.ProxyDirContext#bind(java.lang.String, java.lang.Object)方法中
经过一系列的调用之后在
org.apache.naming.resources.FileDirContext#bind
里面存在文件操作,在文件操作里面传进去的name在
java.io.File#File(java.io.File, java.lang.String)
文件名处会被去除/符号,然后进入
org.apache.naming.resources.FileDirContext#rebind中
在rebind方法中完成文件的写入操作:
写完文件后返回去doPut中查看exists的值,这个值是一开始进行缓存检查的时候设置的,被设置为false,所以返回201的状态码:
根据分析来说,如果PUT请求的uri存在也会经过this.resources.rebind()方法写入文件,只不过返回的状态码是204:
0x06 为什么不能直接使用.jsp文件
上面的web.xml中存在存在另一个JspServlet,只要是.jsp或者.jspx后缀名的uri都会进入到
org.apache.jasper.servlet.JspServlet中
进行处理,JspServlet没有doGet、doPost、doPut方法就会进入service方法中:
而service方法最终会进入
org.apache.jasper.servlet.JspServlet#serviceJspFile方法中
在这里面会去获取jsp文件对应的wrapper,因为没有则直接返回:
最终抛出404的返回状态码:
END
往期经典回顾
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,文章作者和本公众号不承担任何法律及连带责任,望周知!!! |
点赞是鼓励 在看是认同 分享是传递知识
看完点个“在看”分享给更多人
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论