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

发布于 2024-11-11 08:09:20 字数 7975 浏览 3 评论 0

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 实例的具体实现,记录思路和细节,便于以此类推,修改其他内容。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

任性一次

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

安静被遗忘

文章 0 评论 0

喔爱吃橙子

文章 0 评论 0

草莓味的萝莉

文章 0 评论 0

梦里兽

文章 0 评论 0

mb_83J3Cyxa

文章 0 评论 0

时间海

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文