天天看點

Java利用技巧——通過反射修改屬性

作者: 戲中人

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,當在某個位置運作的位置下斷點,到點時,可以檢視請求對象的完整結構,如下圖

Java利用技巧——通過反射修改屬性

檢視請求對象的結構,最終我們定位到了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字段

嘗試尋找原因:開啟調試器,定位到關鍵位置,如下圖

Java利用技巧——通過反射修改屬性

從圖中可以看到,_servlet的類為JettyJspServlet

JettyJspServlet繼承自JspServlet,成員隻有serialVersionUID,這與我們通過通路jsp結果的結果一緻

檢視JspServlet的相關實作代碼,如下圖

Java利用技巧——通過反射修改屬性

從圖中可以看到,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的類型,如下圖

Java利用技巧——通過反射修改屬性

從圖中可以看到,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 修改執行個體的具體實作,記錄思路和細節,便于進行此類推文,其他内容。

繼續閱讀