【Web安全】JSP内存马研究

在这里插入图片描述

前言

最近在研究webshell免杀的问题,到了内存马免杀部分发现传统的Filter或者Servlet查杀手段比较多,不太容易实现免杀,比如有些工具会将所有注册的Servlet和Filter拿出来,排查人员仔细一点还是会被查出来的,所以
我们要找一些其他方式实现的内存马。比如我今天提到的JSP的内存马(虽然本质上也是一种Servlet类型的马) 。

学习资料

JSP加载流程分析

在Tomcat中jsp和jspx都会交给JspServlet处理,所以要想实现JSP驻留内存,首先得分析JspServlet的处理逻辑。

<servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    ...
    </servlet>
...
<servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>

下面分析JspServlet#service方法,主要的功能是接收请求的URL,判断是否预编译,核心的方法是serviceJspFile。

public void service (HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String jspUri = jspFile;
            jspUri = (String) request.getAttribute(
                    RequestDispatcher.INCLUDE_SERVLET_PATH);
            if (jspUri != null) {
      //检查请求是否是通过其他Servlet转发过来的
                String pathInfo = (String) request.getAttribute(
                        RequestDispatcher.INCLUDE_PATH_INFO);
                if (pathInfo != null) {
                    jspUri += pathInfo;
                }
            } else {
       //获取ServletPath和pathInfo作为jspUri
                jspUri = request.getServletPath();
                String pathInfo = request.getPathInfo();
                if (pathInfo != null) {
                    jspUri += pathInfo;
                }
            }
        }
        try {
            //是否预编译
            boolean precompile = preCompile(request);
            //核心方法
            serviceJspFile(request, response, jspUri, precompile);
        } catch (RuntimeException | IOException | ServletException e) {
            throw e;
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(e);
        }
    }

preCompile中只有当请求参数以jsp_precompile开始才会进行预编译,否则不进行预编译。

boolean preCompile(HttpServletRequest request) throws ServletException {
        String queryString = request.getQueryString();
        if (queryString == null) {
            return false;
        }
        //    public static final String PRECOMPILE = System.getProperty("org.apache.jasper.Constants.PRECOMPILE", "jsp_precompile");
        int start = queryString.indexOf(Constants.PRECOMPILE);
        if (start < 0) {
            return false;
        }
        queryString =
            queryString.substring(start + Constants.PRECOMPILE.length());
        if (queryString.length() == 0) {
            return true;             // ?jsp_precompile
        }
        if (queryString.startsWith("&")) {
            return true;             // ?jsp_precompile&foo=bar...
        }
        if (!queryString.startsWith("=")) {
            return false;            // part of some other name or value
        }
       ...
    }

那么预编译的作用是什么?当进行预编译后会怎么样?答案在JspServletWrapper#service中,当预编译后,请求便不会调用对应JSP的servlet的service方法进行处理,所以要想让我们的JSP能正常使用,当然是不要预编译的,默认情况下也不会预编译。

public void service(HttpServletRequest request,
                        HttpServletResponse response,
                        boolean precompile)
            throws ServletException, IOException, FileNotFoundException {
        Servlet servlet;
      ...

            // If a page is to be precompiled only, return.
            if (precompile) {
                return;
     ...
            /*
             * (4) Service request
             */
            if (servlet instanceof SingleThreadModel) {
               // sync on the wrapper so that the freshness
               // of the page is determined right before servicing
               synchronized (this) {
                   ``.service(request, response);
                }
            } else {
                servlet.service(request, response);
            }
      ...

下面再来看serviceJspFile方法,该方法判断JSP是否已经被注册为一个Servlet,不存在则创建JspServletWrapper并put到JspRuntimeContext中,JspServletWrapper.service是核心方法。

private void serviceJspFile(HttpServletRequest request,
                                HttpServletResponse response, String jspUri,
                                boolean precompile)
        throws ServletException, IOException {
//  首先判断JSP是否已经被注册为一个Servlet,ServletWrapper是Servlet的包装类,所有注册的JSP servlet都会被保存在JspRuntimeContext的jsps属性中,如果我们第一次请求这个JSP,当然是找不到wrapper的。
        JspServletWrapper wrapper = rctxt.getWrapper(jspUri);
        if (wrapper == null) {
            synchronized(this) {
                wrapper = rctxt.getWrapper(jspUri);
                if (wrapper == null) {
                 //检查JSP文件是否存在
                    if (null == context.getResource(jspUri)) {
                        handleMissingResource(request, response, jspUri);
                        return;
                    }
                    //创建JspServletWrapper
                    wrapper = new JspServletWrapper(config, options, jspUri,
              
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值