0x00 Preface

---

In the previous article 'Setting Up Zimbra Vulnerability Debugging Environment', we mentioned enumerating JspServletWrapper instances through reflection. This article will take that as an example to detail the implementation approach and specifics, facilitating extrapolation to implement other functionalities.

0x01 Introduction

---

This article will cover the following:

  • Common operations in reflection
  • Obtaining all fields of a class
  • Obtaining all methods of a class
  • Invoking class methods
  • Implementation details of enumerating JspServletWrapper instances

0x02 Common Operations in Reflection

---

1. Obtaining all fields of a class

getField():

Can retrieve public fields from both this class and its parent class

getDeclaredField():

Can retrieve all fields from this class

Here is an example code using the Zimbra environment as an example

(1) Get all fields of the request object

<%@ page import="java.lang.reflect.Field" %>
<%
Field[] fields=request.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "
");
}
%>

(2) Get all fields of the parent class of the request object

<%@ page import="java.lang.reflect.Field" %>
<%
Field[] fields=request.getClass().getSuperclass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "
");
}
%>

2. Get all methods of a class

getMethods():

Can obtain public methods (modified by the public modifier) from this class as well as its parent class or parent interface

getDeclaredMethods():

Can obtain all methods in this class, including private, protected, default, and public methods

Here, using the Zimbra environment as an example, provide sample code

(1) Get all methods of the request object

<%@ page import="java.lang.reflect.Field "%>
<%@ page import="java.lang.reflect.Method "%>
<%
Method[] methods = request.getClass().getDeclaredMethods();
for(Method method:methods){
out.print(method.getName() + "
");
}
%>

(2) Get all methods of the parent class of the request object

<%@ page import="java.lang.reflect.Field "%>
<%@ page import="java.lang.reflect.Method "%>
<%
Method[] methods = request.getClass().getSuperclass().getDeclaredMethods();
for(Method method:methods){
out.print(method.getName() + "
");
}
%>

3. Invoking specific methods of a class

Here, using the Zimbra environment as an example, sample code is provided

After setting up the Zimbra vulnerability debugging environment, locate the getHeader(String name) method of the request object. The code details are as follows:

public String getHeader(String name) {
org.eclipse.jetty.http.MetaData.Request metadata = this._metaData;
return metadata == null ? null : metadata.getFields().get(name);
}

Referring to the code details, the parameter type is String

Invoke the getHeader(String name) method of the request object with the parameter "User-Agent". The implementation code is as follows:

<%@ page import="java.lang.reflect.Field "%>
<%@ page import="java.lang.reflect.Method "%>
<%
Method m1 = request.getClass().getDeclaredMethod("getHeader", String.class);
out.println(m1.invoke(request, "User-Agent"));
%>

0x03 Implementation Details of Enumerating JspServletWrapper Instances

---

1. Setting Breakpoints

Select the file servlet-api-3.1.jar, then navigate to javax.servlet->http->HttpServlet.class. Set a breakpoint at an appropriate location. When execution reaches the breakpoint, you can view the complete structure of the request object, as shown in the figure below.

Alt text

By examining the structure of the request object, we ultimately located the position of the JspServletWrapper instance. The intuitive mapping is: request->_scope->_servlet->rctxt->jsps

Next, we need to follow this mapping and obtain the JspServletWrapper instance through multiple reflections.

(1) Reading all fields of the request object

<%@ page import="java.lang.reflect.Field" %>
<%
Field[] fields=request.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "
");
}
%>

Find _scope in the echoed results

(2) Obtain the _scope instance from the request object and enumerate its fields again

<%@ page import="java.lang.reflect.Field" %>
<%
Field f = request.getClass().getDeclaredField("_scope");
f.setAccessible(true);
Object obj1 = f.get(request);
Field[] fields=obj1.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "
");
}
%>

Find _servlet in the echoed results

(3) Obtain the _servlet instance and enumerate its fields again

<%@ page import="java.lang.reflect.Field" %>
<%
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);
Field[] fields=obj2.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "
");
}
%>

The echo result is: serialVersionUID

There is no rctxt field here

Attempting to find the cause: Start the debugger and locate the key position, as shown in the figure below

Alt text

As can be seen from the figure, the class of _servlet is JettyJspServlet

JettyJspServlet inherits from JspServlet, and the only member variable is serialVersionUID, which is consistent with the results we obtained by accessing the JSP page

Review the relevant implementation code of JspServlet, as shown in the figure below

Alt text

As can be seen from the figure, rctxt is a member variable of JspServlet, with a private attribute, so the subclass JettyJspServlet cannot inherit the member variable rctxt

Here we can directly select the parent class of the _servlet instance to enumerate fields

(4) Select the parent class of the _servlet instance to enumerate fields

<%@ page import="java.lang.reflect.Field" %>
<%
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);
Field[] fields = obj3.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "
");
}
%>

jsps can be found in the echo results

(5) Obtain the jsps instance and enumerate fields

Open the debugger and check the type of jsps, as shown in the figure below

Alt text

As can be seen from the figure, the class of jsps is ConcurrentHashMap. Here, only all Keys need to be enumerated.

After adding the required import packages, the final implementation code is obtained:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page import="java.util.*" %>
<%
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);
Enumeration enu = obj4.keys();
while (enu.hasMoreElements()) {
out.println(enu.nextElement() + "
");
}
%>

Note: Delete the specified JspServletWrapper instance

Simply call the remove method of ConcurrentHashMap

Example code:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page import="java.util.*" %>
<%
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);
obj4.remove("/test.jsp");
%>

0x04 Summary

---

This article introduces the specific implementation of enumerating JspServletWrapper instances through reflection, documenting the approach and details to facilitate extrapolation for modifying other content.