Spring1利用链分析

1
最近在复习反序列化利用链的知识,尽管之前分析过spring1的利用链,再回头看还是发现其构造过程非常的精彩,再加上分析Spring利用链的文章比较少,因此决定写下Spring1反序列化利用链的分析过程。

分析过程

​ 漏洞的sink在MethodInvokeTypeProvider#readObject中这里通过反射完成方法调用,按照我们之前分析利用链的经验,遇到任意调用的反射肯定是想往Templates#newTransformer上构造,因为只有TemplatesImpl才能触发类加载并执行任意代码而不仅仅是简单的命令执行。

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());
}

​ 要完成这种方式的利用,我们需要让findMethod返回newTransformer方法,并且让this.provider.getType()返回我们构造好的TempletesImpl对象。

1
2
3
4
5
6
7
8
9
10
11
12
public static Object invokeMethod(Method method, Object target) {
return invokeMethod(method, target);
}

public static Object invokeMethod(Method method, Object target, Object... args) {
try {
return method.invoke(target, args);
} catch (Exception var4) {
handleReflectionException(var4);
throw new IllegalStateException("Should never get here");
}
}

​ 那么如何让this.provider.getType()返回一个TemplatesImpl对象呢?肯定是需要使用动态代理,当调用到getType时通过InvocationHandler的处理让目标返回TemplatesImpl。在AnnotationInvocationHandler#invoke中可以通过控制memberValues的属性值来控制返回的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else if (var5.length != 0) {
throw new AssertionError("Too many parameters for an annotation method");
...
default:
//从memberValues属性中获取对象并返回
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}

return var6;
}

​ 但是我们回头去看yso中spring1的利用链却发现并没有我们想象的那么简单,为什么会这么复杂?直接在创建动态代理时将getType的value换为templates不可以吗?

image-20220521112429951

​ 实际上是不行的,因为getType方法已经明确了返回的对象类型是Type类型而我们直接返回TemplatesImpl类型会导致类型转换错误而失败。所以当调用getType时我们还要返回一个动态代理对象。这个对象要同时实现TypeTemplates接口。那么要选哪个InvocationHandler呢?如何才能在反射调用这个代理对象的newTransformer方法时能调用到我们的TemplatesImpl对象的newTransformer方法。也就是说我们需要在这个InvocationHandler#invoke方法中能够获取TemplatesImpl对象并反射调用。

image-20220521112825003

​ 漏洞作者找到了ObjectFactoryDelegatingInvocationHandler,只要我们能控制this.objectFactory.getObject()的返回结果为构造好的TemplateImpl对象即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals("equals")) {
return proxy == args[0];
} else if (methodName.equals("hashCode")) {
return System.identityHashCode(proxy);
} else if (methodName.equals("toString")) {
return this.objectFactory.toString();
} else {
try {
return method.invoke(this.objectFactory.getObject(), args);
} catch (InvocationTargetException var6) {
throw var6.getTargetException();
}
}

​ 所以我们要创建objectFactory的动态代理并且让Handler为AnnocationInvocationHandler并且设置memberValue属性中getObject为key,value为TemplatesImpl对象。

总结

​ Spring1这条链算是把动态代理玩明白了,通过嵌套了多层代理。通过创建同时实现TypeTempletes接口的代理类来使目标可以获取到newTransformer的Method对象,最后又利用ObjectFactoryDelegatingInvocationHandler动态获取TemplatesImpl对象反射调用newTransformer完成利用。