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.

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.

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

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

In summary, we can derive the implementation code for obtaining the compilation file path through reflection as follows:
<%@ page import="java.lang.reflect.Field" %> |
The code to delete compiled files is as follows:
<%@ page import="java.lang.reflect.Field" %> |
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.

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); |
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.