【Web安全】JSP内存马研究

------------------------------------------------------------------------------------------------------------------------------------------

前言

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

【学习资料】

JSP加载流程分析

在Tomcat中jspjspx都会交给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作为jspUrijspUri = 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;}//创建JspServletWrapperwrapper = new JspServletWrapper(config, options, jspUri, rctxt);//添加wrapper到JspRuntimeContext的jsps属性中rctxt.addWrapper(jspUri,wrapper);}}}try {//核心方法wrapper.service(request, response, precompile);} catch (FileNotFoundException fnfe) {handleMissingResource(request, response, jspUri);}} 

JspServletWrapper.service主要做了如下操作。

  • 根据jsp生成java文件并编译为class* 将class文件注册为servlet* 调用servlet.service方法完成调用JSP生成java和class文件主要由下面的代码完成,这里的options.getDevelopment()代表的是部署模式。

tomcat的开发模式和生产模式的设定是通过conf文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值