JAVA安全—反射机制攻击链类对象成员变量方法构造方法

前言

还是JAVA安全,哎,真的讲不完,太多啦。

今天主要是讲一下JAVA中的反射机制,因为反序列化的利用基本都是要用到这个反射机制,还有一些攻击链条的构造,也会用到,所以就讲一下。

什么是反射

Java提供了一套反射API,该API由Class类与java.lang.reflect类库组成。
该类库包含了Field、Method、Constructor等类。
对成员变量,成员方法和构造方法的信息进行的编程操作可以理解为反射机制。

从官方定义中就能找到其存在的价值,在运行时获得程序程序集中每一个类型的成员和成员的信息,从而动态的创建、修改、调用、获取其属性,而不需要事先知道运行的对象是谁。划重点:在运行时而不是编译时。(不改变原有代码逻辑,自行运行的时候动态创建和编译即可

参考连接:文章 - JAVA安全基础(二)-- 反射机制 - 先知社区

项目创建

创建一个Java项目,名字叫ReflectDemo。

我这里选择JAVAEE8。

把这几个东西删除掉,因为没啥用。

新建一个类叫User。

先写入以下代码,这里说明一下以下四个为成员变量,分别对应3个不同的属性,公共属性、私有属性、保护属性。

 public String name = "wlw";public int age = 20;private String gender = "man";protected String job = "sec";

再写入成员方法,一个为public属性,一个为protected属性。

public void userinfo(String name, int age, String gender, String job) {this.name = name;this.age = age;this.gender = gender;this.job = job;}
protected void users(String name, String gender) {this.name = name;this.gender = gender;System.out.println("user的成员方法"+name);System.out.println("user的成员方法"+gender);}

最后再写入两个构造方法,可以看到方法名字都是User,和我们类的名字一样。

public User(){}public User(String name){System.out.println("my name"+name);}private User(String name,int age){System.out.println(name);System.out.println(age);}

Class对象类获取

OK我们的User类已经写好了,现在来获取它里面的方法。可能有人有疑惑我都知道这个类名叫User为啥还要获取它呢,是这样的,你要对一个类进行操作或者调用,首先必须要获取这个类的类名才行进行下一步,并不是说我们人知道类名就行了,还得让代码知道。

根据全限定类名获取

我们直接Class.forName(“全路径类名”),运行起来成功获取类名。

 public static void main(String[] args) throws ClassNotFoundException {Class aClass = Class.forName("com.sf.maven.reflectdemo.User");System.out.println(aClass);}

根据类名获取

第二种是直接类名.class即可获取到类名

Class bClass = User.class;
System.out.println(bClass);

根据对象获取

第三种是对象.getClass()直接获取类名。

User user = new User();
Class aClass1 = user.getClass();
System.out.println(aClass1);

通过类加载器获取

第四种就是我们可以通过类加载器 ClassLoader.getSystemClassLoader().loadClass(“全路径类名”); 来获取类名。

 ClassLoader clsload=ClassLoader.getSystemClassLoader();Class aClass2 = clsload.loadClass("com.sf.maven.reflectdemo.User");System.out.println(aClass2);

利用反射获取成员变量

前面我们已经获取到类名了,现在我们利用反射来获取类里面的成员变量。

新建一个类叫GetField,在里面写入我们获取成员变量的方法,先在开头加入我们获取类名的代码才行。

常见的获取成员变量的方法有以下几种。

获取所有公共成员变量

可以看到输出的是我们在User定义的两个public成员变量,name和age。

Field[] fields = aClass.getFields();
for (Field field : fields) {System.out.println(field);}

获取所有成员变量

可以看到三个不同属性的成员变量均获取到。

Field[] fields1 = aClass.getDeclaredFields();
for (Field field : fields1) {System.out.println(field);}

获取单个公共成员变量

可以看到只获取了一个name公共成员变量。

//获取单个公共成员变量Field field2 = aClass.getField("name");System.out.println(field2);

获取任意单个成员变量

可以看到无论是什么属性的成员变量都可以获取到。

 //获取单个成员变量Field field3 = aClass.getDeclaredField("gender");System.out.println(field3);

成员变量的值修改和获取

看完获取成员变量了,我们再看一下对成员变量的值进行修改还有获取。

首先创建一个对象也就是获取类名,然后获取age这个公共的成员变量,接着获取user对象的age值,最后输出。

可能这里大家有点不明白,前面开头我们不是已经获取类名了,为啥这里还要获取啊。是这样的,前面获取的类名我们只是为了获取其里面的age成员变量,此时我们的age成员变量已经赋值给field4了,field4.get(user)就是获取user类里面的age值,换句话说后面的是我们要从这个User类里面得到这个age的值,也就是说我们new一个其它的类,只要这个类里面有age的成员变量,也会被获取!!!(讲的有点乱,不对还请指正)

//获取成员变量的值User user = new User();Field field4 = aClass.getField("age");Object a = field4.get(user);System.out.println(a);

接着对User类里面age的值进行修改,通过 field4.set(user,30); 修改user类里面的age值,可以看到输出为30,但是我们并没有去修改user类age成员变量的代码,这就是JAVA反射机制!!!

field4.set(user,30);
Object b = field4.get(user);
System.out.println(b);

利用反射获取构造方法

获取类中的构造方法也是有四种方式。

新建一个类叫GetConstructor,用来专门写获取构造方法的代码,同样记得再开头加上获取类名的代码才行。

获取所有公共构造方法

可以看到获取到了我们前面写好的User(String name) 和 USer() 这两个公共的成员方法。

//获取公共构造方法Constructor[] constructors1 = class1.getConstructors();for (Constructor constructor : constructors1) {System.out.println(constructor);}

获取所有构造方法

可以看到无论是public属性还是private属性的构造方法都被获取到了。

//获取所有构造方法Constructor[] constructors2 = class1.getDeclaredConstructors();for (Constructor constructor : constructors2) {System.out.println(constructor);}

获取单个公共构造方法

这个和获取所有的公共构造方法的代码差不多,只不过是指定了 String 类型的构造方法,前面我们写好的String类型的公共构造方法就只有 User(String name) 所以就返回了这个。

//获取单个公共构造方法Constructor constructor3 = class1.getConstructor(String.class);System.out.println(constructor3);

获取单个私有构造方法

和上一个的差不多,只不过是多了个Declared而已。

//获取单个私有构造方法
Constructor constructor4 = class1.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor4);

对构造方法进行操作

setAccessible(true) 临时开启对私有的访问,newInstance 使用构造方法创建对象,传递参数,允许在运行时通过 Constructor 对象调用类的构造方法。

代码逻辑和上面的成员变量修改差不多,上面是从User类里面找age这个成员变量,这里是从User类里面找到符合的构造方法。可以看到我们的两个参数成功传入到 User(String name,int age) 这个构造方法里面,并且成功调用这个构造方法。

//对构造方法进行操作
Constructor constructor5 = class1.getDeclaredConstructor(String.class, int.class);
//临时开启对私有构造方法的访问
constructor5.setAccessible(true);
User uu = (User) constructor5.newInstance("wlwnb666",30);
System.out.println(uu);

利用反射获取成员方法

获取成员方法的方式也是四种,新建一个类叫GetMethod。

获取包括继承的所有公共成员方法,可以看到输出有很多公共成员方法,这是由于它连JAVA中自带的公共成员方法也一并输出了,并不单单输出我们自己写的。

//获取包括继承的所有公共成员方法Method[] methods1 = class1.getMethods();for (Method method : methods1) {System.out.println(method);}

获取不包括继承的所有成员方法,这里输出了所有我们自己写的成员方法,并没有输出JAVA内置的。

//获取不包括继承的所有成员方法Method[] methods2 = class1.getDeclaredMethods();for (Method method : methods2) {System.out.println(method);}

获取单个成员方法,这个要指定我们获取的成员方法名称为 name ,参数类型也要对应上才行。

 //获取单个成员方法
Method method3= class1.getDeclaredMethod("users", String.class, String.class);
System.out.println(method3);

和上面几乎一样。

//获取单个公共成员方法
Method method4= class1.getMethod("userinfo", String.class, int.class, String.class, String.class);
System.out.println(method4);

对成员方法进行调用,对我们前面写好的 user 成语方法进行调用。

 //调用成员方法
User user = new User();
Method method5= class1.getDeclaredMethod("users", String.class, String.class);
method5.invoke(user,"wlwnb666","sex");

反序列化链条构造

OK,反射的知识点基本都讲完了,那现在我们来构造以下利用链。

先简单写一个调用计算机的命令执行,这个是调用JAVA中自带的包,我们称之为原生调用。

但是我们想一想,如果是第三方的包,是不是就得要用反射机制来得到命令执行,由于这里我没有引入第三方的包,所以我们就用JAVA自带的包来做一下通过反射实现命令执行的演示,当作是外部的包即可。

首先我们可以看到这个Runtime.getRuntime().exec() 这个命令执行方法是来自 java,lang.Runtime这个类的。

那么我们就先获取类名和所有的公共成员方法,记得这里要把路径写全,不能只写Runtime。

可以看到有很多,找到getRuntime这个方法。

查询一下。

现在我们单独把这个getRuntime 获取出来。

此外我们还需获取exec方法,由于exec 需要传参String类型,所以要加String.class。

Method exec = class1.getMethod("exec", String.class);

整个链条如下,由于exec方法属于实例方法,所以所以 exec. invoke 的第一个参数是 Runtime 实例。

这里可能有人不理解第三第四行代码,一开始我也不是很懂,后来查了一下大致理解了。首先我们要知道什么是实例对象,实例指的是通过某个类(Class)创建出来的具体对象,例如:Use user = new Use() 这样就创建了一个实例。那么回到我们的代码,可以看到 getRuntime 方法返回了 currentRuntime,而currentRuntime 正是开头创建的实例,也就是说getRuntime 方法返回的是一个实例。

那么回到我们构造的链条,Object runtime = method4.invoke(class1); 调用 getRuntime 方法,并且返回一个实例赋值给 runtime ,所以 exec.invoke(runtime, "calc.exe"); 第一个参数是runtime,第二个参数才是命令。

除了上面的说到的链条构造样子,还可以这样子去构造,不过感觉没有第一种简单明了。

// 使用 Class.forName 获取 Runtime 类
Class c1 = Class.forName("java.lang.Runtime");// 获取 Runtime 类的默认构造方法
Constructor m = c1.getDeclaredConstructor();// 设置构造方法为可访问
m.setAccessible(true);// 使用反射调用 Runtime 类的 exec 方法,执行系统命令 "calc"
c1.getMethod("exec", String.class).invoke(m.newInstance(), "calc");

不安全的反射对象

指应用程序使用具有反射功能的外部输入来选择要使用的类或代码,
可能被攻击者利用而输入或选择不正确的类。绕过身份验证或访问控制检查

参考连接:悟空云课堂 | 第七期:不安全的反射漏洞 - 知乎

文章 - JAVA反序列化 - Commons-Collections组件 - 先知社区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/68045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TfidfVectorizer

TF-IDF / Term Frequency - Inverse Document Frequency 作用:是自然语言处理NLP中常用的文本特征提取工具,用于将文本数据转换为数据向量。 核心思想:是通过统计词频和逆文档频率来量化词语在文本中的重要性。 T F − I D F ( t , d ) T F…

DeepSeek-R1 论文解读:强化学习如何 “炼” 出超强推理模型?

深度解析DeepSeek-R1:强化学习驱动大语言模型推理能力新突破 论文链接:DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning 在大语言模型(LLMs)飞速发展的当下,提升模型推理能力成…

【数据结构】循环链表

循环链表 单链表局限性单向循环链表判断链表是否有环思路code 找到链表入口思路代码结构与逻辑 code 单链表局限性 单链表作为一种基本的数据结构,虽然在很多场景下都非常有用,但它也存在一些局限性: 单向访问:由于每个节点仅包含…

ip属地是手机号还是手机位置?一文理清

在数字化和网络化的今天,IP属地这一概念逐渐成为了人们关注的焦点。特别是在社交媒体和在线平台上,IP属地的显示往往让人联想到用户的地理位置。然而,关于IP属地到底与手机号还是手机位置有关,却存在着不少误解和混淆。本文将深入…

【嵌入】基于nomic-embed-text-v1.5和HuggingFaceEmbeddings实现

测试代码 model_name = /media/zhangbin/DATA/DataCache/nomic-ai/nomic-embed-text-v1.5import osos.environ[HF_HOME] = /media/zhangbin/DATA/DataCache/#os.environ["TRANSFORMERS_CACHE"] = "/media/zhangbin/DATA/DataCache/" # 确保目录结构正确 if…

离散时间傅里叶变换(DTFT)公式详解:周期性与连续性剖析

摘要 离散时间傅里叶变换(DTFT)是数字信号处理领域的重要工具,它能将离散时间信号从时域转换到频域,揭示信号的频率特性。本文将深入解读DTFT公式,详细阐述其具有周期性和连续性的原因,帮助读者全面理解DT…

哈希表与散列表的原理及C++实现

1. 什么是哈希表? 哈希表(Hash Table)是一种高效的数据结构,用于存储键值对(Key-Value Pairs)。它通过哈希函数(Hash Function)将键(Key)映射到一个固定大小…

图像分类与目标检测算法

在计算机视觉领域,图像分类与目标检测是两项至关重要的技术。它们通过对图像进行深入解析和理解,为各种应用场景提供了强大的支持。本文将详细介绍这两项技术的算法原理、技术进展以及当前的落地应用。 一、图像分类算法 图像分类是指将输入的图像划分为…

前端框架中 HTML 的应用技巧:React、Vue、Angular 深度解析

系列文章目录 01-从零开始学 HTML:构建网页的基本框架与技巧 02-HTML常见文本标签解析:从基础到进阶的全面指南 03-HTML从入门到精通:链接与图像标签全解析 04-HTML 列表标签全解析:无序与有序列表的深度应用 05-HTML表格标签全面…

Vue - customRef 自定义ref

customRef 作用:创建一个自定义的 ref , 并对其依赖项跟踪和更新触发进行逻辑控制。 在原生 ref 的基础上,添加自己的逻辑等... 在 Vue 3 中,customRef 是一个用于创建自定义响应式引用的 API。它允许开发者控制和优化响应式引用的行为&…

数字化转型:概念性名词浅谈(第四讲)

​大家好,本篇文章是在新年之际写的,所以在这里先给大家拜个年。 今天要介绍的名词为ETL: ETL,是英文Extract-Transform-Load的缩写,用来描述将数据从来源端经过抽取(extract)、转换(transfor…

UE学习日志#22 C++笔记#8 基础复习8 string和string_view2

1 std::string_view类 string_view基本上就是const string&的简单替代品,但不会产生开销。他不复制字符串。 string_view添加了remove_prefix(size_t)和remove_suffix(size_t)方法,前者将起始指针给定的偏移量来收缩字符串,后者则将结尾指…

UVM factory机制

目录 1. factory-register 1.1 uvm_object_registry#(type T=uvm_object, string Tname="") 1.1 uvm_default_factory::register 2. factory-override 2.1 set_type_override(uvm_object_wrapper override_type) 2.2 set_inst_override(uvm_object_wrapper ove…

Spring MVC学习——发送请求(@RequestMapping注解及请求参数绑定)

前言 Spring MVC作为Spring框架中的核心组件之一,其强大的功能在于能简洁高效地处理HTTP请求和响应。在开发Web应用时,理解和正确使用Spring MVC的注解,尤其是RequestMapping注解,至关重要。本文将详细讲解RequestMapping注解的使…

C# Action和 Func的用法

C#中的数据类型 函数数据类型 Action 是一个数据类型 但是是没有返回值得函数数据类型 Func 用于指定一个有返回值的委托 internal class Program{static void Main(string[] args){TT.F1(NoVoid);TT.F2(Void1);Void2(() > { Console.WriteLine("Void2执行了");…

C++中的模板(上)

C中的模板(上) 模板参数和函数参数是很像的,函数参数定义的形参对象,而模板参数定义的是类型. 模板分为函数模板和类模板 函数模板 一个交换两个数的函数模板: template<class T> // 此处typename和class是等价的 void Swap(T &a, T &b) {T temp a;a b;b …

MySQL面试题----如何进行 MySQL 数据库备份与恢复

MySQL 数据库备份 1. 使用 mysqldump 工具(逻辑备份) 全量数据库备份 该方式会备份指定数据库中的所有数据和表结构。在命令行中输入以下命令,将 username 替换为你的 MySQL 用户名,password 替换为对应的密码,database_name 替换为要备份的数据库名,backup.sql 为备份文…

Java集合面试总结(题目来源JavaGuide)

问题1&#xff1a;说说 List,Set,Map 三者的区别&#xff1f; 在 Java 中&#xff0c;List、Set 和 Map 是最常用的集合框架&#xff08;Collection Framework&#xff09;接口&#xff0c;它们的主要区别如下&#xff1a; 1. List&#xff08;列表&#xff09; 特点&#xf…

deepseek接入pycharm 进行AI编程

要将DeepSeek接入PyCharm进行AI编程,可以按照以下步骤操作: ### 1. 获取DeepSeek API访问权限 DeepSeek通常以API的形式对外提供服务,你需要在其官方网站注册账号,申请API访问权限。在申请通过后,会获得API密钥(API Key),这是后续调用API的关键凭证。 ### 2. 安装必要…

奥迪改名风波再起,A6L能否率队创下新奇迹

文/王俣祺 导语&#xff1a;春节假期刚过&#xff0c;奥迪的车型命名规则又变了。在如今以内卷为主基调的环境下&#xff0c;车型改名可不是小事&#xff0c;而奥迪的这次调整背后藏着许多深意&#xff0c;也预示着2025年奥迪在产品布局上的新动向。 改名能否“改命” 回溯到…