2

Java利用技巧——通过反射修改属性

 2 years ago
source link: https://3gstudent.github.io/Java%E5%88%A9%E7%94%A8%E6%8A%80%E5%B7%A7-%E9%80%9A%E8%BF%87%E5%8F%8D%E5%B0%84%E4%BF%AE%E6%94%B9%E5%B1%9E%E6%80%A7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java利用技巧——通过反射修改属性

05 Jan 2022

0x00 前言

在上篇文章《Zimbra漏洞调试环境搭建》提到了通过反射枚举JspServletWrapper实例的实现,本文将要以此为例,详细介绍实现的思路和细节,便于以此类推,实现其他功能。

0x01 简介

本文将要介绍以下内容:

  • 反射中的常用操作
  • 获得类的所有字段
  • 获得类的所有方法
  • 调用类的方法
  • 枚举JspServletWrapper实例的实现细节

0x02 反射中的常用操作

1.获得类的所有字段

getField():

能够获取本类以及父类中的public字段

getDeclaredField():

能够获取本类中的所有字段

这里以Zimbra环境为例,给出示例代码

(1)获取request对象的所有字段

<%@ 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)获取request对象的父类的所有字段

<%@ 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.获得类的所有方法

getMethods():

能够获取本类以及父类或者父接口中的公共方法(public修饰符修饰的)

getDeclaredMethods():

能够获取本类中的所有方法,包括private、protected、默认以及public的方法

这里以Zimbra环境为例,给出示例代码

(1)获取request对象的所有方法

<%@ 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)获取request对象的父类的所有方法

<%@ 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环境为例,给出示例代码

搭建好Zimbra漏洞调试环境后,定位request对象的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

调用request对象的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,在合适的位置下断点,当运行到断点时,可以查看request对象的完整结构,如下图

Alt text

查看request对象的结构,我们最终定位到了JspServletWrapper实例的位置,直观的映射为:request->_scope->_servlet->rctxt->jsps

接下来,我们需要按照这个映射,通过多次反射最终获得JspServletWrapper实例

(1)读取request对象的所有字段

<%@ 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)从request对象获得_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字段

尝试寻找原因:开启调试器,定位至关键位置,如下图

Alt text

从图中可以看到,_servlet的类为JettyJspServlet

JettyJspServlet继承自JspServlet,成员变量只有serialVersionUID,这与我们通过访问jsp页面得到的结果一致

查看JspServlet的相关实现代码,如下图

Alt text

从图中可以看到,rctxt为JspServlet的成员变量,属性为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的类型,如下图

Alt text

从图中可以看到,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>"); 
    }  
%>

补充:删除指定JspServletWrapper的实例

只需要调用ConcurrentHashMap的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 小结

本文介绍了通过反射枚举JspServletWrapper实例的具体实现,记录思路和细节,便于以此类推,修改其他内容。


LEAVE A REPLY


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK