2022 年 3 月 8 日
0x00 前言
在上篇文章《Zim 開源環境實作實作》中提到了通過反射枚舉 JServletWrapper 執行個體的實作,本文将通過各種細節、細節和細節來實作,以便于介紹諸如此類、調試其他功能。
0x01簡介
本文将要介紹以下内容:
- 反射中的常用操作
- 獲得同類的所有領域
- 獲得類的所有方法
- 類的方法
- 枚舉JspServletWrapper執行個體的實作細節
0x02 反射中的常用操作
1. 獲得類的所有領域
擷取字段():
能夠擷取本類以及父類中的公共場所
getDeclaredField():
能夠擷取本類中的所有領域
這裡以 Zimbra 環境,提供示例代碼
(1) 請求對象的所有擷取
<%@ page import="java.lang.reflect.Field" %>
<%
Field[] fields=request.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "<br>");
}
%>
(2) 擷取對象的父類的所有字段
<%@ 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() + "<br>");
}
%>
2.獲得類的所有方法
擷取方法():
公共擷取本類以及父類或父接口中的方法(公共物質符物質的)
getDeclaredMethods():
擷取本類中的所有方法,包括私有的、受保護的、預設的以及公共的方法
(1)擷取請求對象的所有方法
<%@ 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() + "<br>");
}
%>
(2)擷取請求對象的父類的所有方法
<%@ 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() + "<br>");
}
%>
3.調用類的指定方法
類似問題的解決方法Zimbra漏洞,解決問題的getHeader(String name)方法,細節:
public String getHeader(String name) {
org.eclipse.jetty.http.MetaData.Request metadata = this._metaData;
return metadata == null ? null : metadata.getFields().get(name);
}
對照組代碼細節,參數類型為String
調用請求對象的getHeader(String name)方法,參數為"User-Agent",實作如下:
<%@ 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 枚舉JspServletWrapper執行個體的實作細節
1.下斷點
選擇檔案servlet-api-3.1.jar,截斷點javax.servlet-> http-> HttpServlet.class,當在某個位置運作的位置下斷點,到點時,可以檢視請求對象的完整結構,如下圖
檢視請求對象的結構,最終我們定位到了JspServlet執行個體的位置,映射的Wrapper為:request-> _scope-> _servlet-> rctxt->jsps
,需要按照這個映射,通過多次發射我們最終獲得JspServletWrapper執行個體
(1) 讀取請求對象的所有字段
<%@ page import="java.lang.reflect.Field" %>
<%
Field[] fields=request.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
out.println(fields[i].getName() + "<br>");
}
%>
在回顯的結果中能夠找到_scope
(2) 從請求對象獲得_scope執行個體并再次枚舉字段
<%@ 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() + "<br>");
}
%>
在回顯的結果中能夠找到_servlet
(3)擷取_servlet執行個體并重新枚舉字段
<%@ 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() + "<br>");
}
%>
回顯的結果為:serialVersionUID
這裡沒有rctxt字段
嘗試尋找原因:開啟調試器,定位到關鍵位置,如下圖
從圖中可以看到,_servlet的類為JettyJspServlet
JettyJspServlet繼承自JspServlet,成員隻有serialVersionUID,這與我們通過通路jsp結果的結果一緻
檢視JspServlet的相關實作代碼,如下圖
從圖中可以看到,rctxt 的成員,屬性為private,是以子JettyJspServlet 不能繼承的成員變量為rctxt
這裡可以指定我們_servlet的父類執行個體直接進行枚舉
(4)_servlet對象的父類進行枚舉
<%@ 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() + "<br>");
}
%>
在回顯的結果中能夠找到jsps
(5)擷取jsps執行個體并枚舉字段
開啟調試器,檢視jsps的類型,如下圖
從圖中可以看到,jsps的類為ConcurrentHashMap,這裡隻需要枚舉出所有的Key即可
添加需要導入的包後,最終實作代碼:
<%@ 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() + "<br>");
}
%>
補充:删除指定JspServletWrappers的執行個體
隻需調用ConHashMap的remove方法即可
示例代碼:
<%@ 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 小結
本文通過反射枚舉 JspServ 修改執行個體的具體實作,記錄思路和細節,便于進行此類推文,其他内容。