Plataforma experimental de campo de tiro de defensa y ataque de red del Instituto de Software de la Universidad de Shandong (9)


Prefacio:

Después de completar la escritura del entorno de vulnerabilidad de inyección SQL y RCE, la siguiente tarea es encontrar una manera de construir un entorno de vulnerabilidad de deserialización de Java, implementarlo con código y ejecutar la prueba.
Para las vulnerabilidades de deserialización, es más fácil usar el lenguaje php para escribir el entorno para la reproducción de vulnerabilidades, pero nuestro entorno de proyecto está escrito en lenguaje Java, por lo que necesitamos encontrar una manera de construir vulnerabilidades de deserialización de Java. Al mismo tiempo, debido a que la vulnerabilidad de deserialización de Java es difícil de entender, lleva mucho tiempo aprender el contenido básico, como el principio y la utilización de esta vulnerabilidad. Por lo tanto, este blog registra principalmente el conocimiento básico de las vulnerabilidades de deserialización de Java, incluida la introducción de vulnerabilidades, el daño, los métodos de utilización y cómo prevenirlas.


1. Introducción a la deserialización de Java

1.1 Introducción

1.1.1 ¿Qué es serialización y deserialización?

La serialización de Java se refiere al proceso de convertir objetos de Java en secuencias de bytes;

La deserialización de Java hace referencia al proceso de restauración de una secuencia de bytes en un objeto de Java;


1.1.2 ¿Por qué utilizar serialización y deserialización?

Antes de usar la serialización y la deserialización, comprendamos los dos usos de la serialización de objetos:

  1. Guarde permanentemente la secuencia de bytes del objeto en el disco duro, generalmente en un archivo;
  2. Una secuencia de bytes para transmitir un objeto a través de la red.

¿Podemos pensar en cuál sería la situación si no hubiera serialización?

Ejemplo:

Objetos de sesión de sesión en el servidor web, cuando hay 100.000 usuarios accediendo simultáneamente, puede haber 100.000 objetos de sesión, obviamente la memoria puede ser demasiado en este caso.

Por lo tanto, el contenedor web serializará primero algunas sesiones, les permitirá dejar espacio en la memoria, las serializará en el disco duro y restaurará los objetos guardados en el disco duro en la memoria cuando sea necesario llamarlos.

Sabemos que cuando dos procesos se comunican de forma remota, pueden enviarse varios tipos de datos entre sí, incluidos texto, imágenes, audio, video, etc., y estos datos se transmitirán a través de la red en forma de secuencias binarias.

La misma serialización y deserialización realizan la transferencia de objetos entre procesos . El remitente necesita convertir el objeto Java en una secuencia de bytes antes de que pueda enviarse a la red; el receptor necesita restaurar la secuencia de bytes en un objeto Java. .

Resumen preliminar: serialización y deserialización de Java, primero, realice la persistencia de los datos, a través de la serialización, los datos se pueden almacenar permanentemente en el disco duro; segundo, use la serialización para realizar la comunicación remota, es decir, transfiera objetos en la red secuencia de bytes .


1.1.3, ¿Qué es la vulnerabilidad de deserialización de Java?

Las vulnerabilidades de deserialización de Java son uno de los tipos más comunes de vulnerabilidades relacionadas con Java y son el foco de atención de los trabajadores de ciberseguridad. La búsqueda de la palabra clave serialized en cve tiene un total de 174 registros, de los cuales 83 están relacionados con java; la búsqueda de deserialized tiene un total de 20 registros, de los cuales 10 están relacionados con java. Estos marcos y componentes con vulnerabilidades de deserialización incluyen el conocido spring, que también tiene muchos componentes básicos en el proyecto de código abierto de Apache. Por ejemplo, Colecciones de Apache Commons. Una gran cantidad de estos componentes básicos están referenciados por otros marcos o componentes.Una vez que ocurre una vulnerabilidad, causará un incidente de seguridad de red a gran escala con consecuencias muy graves.

Los objetos que deben serializarse deben implementar la interfaz @serializable. Cabe señalar que si el método writeObject()|readObject() existe en la clase que se serializa o deserializa , se llamará antes de serialization|deserialization . Esta suele ser una característica importante que provoca vulnerabilidades de deserialización.

Si la aplicación Java deserializa la entrada del usuario, es decir, los datos que no son de confianza, el atacante puede construir una entrada maliciosa para hacer que la deserialización genere objetos inesperados, y los objetos inesperados pueden provocar la ejecución de código arbitrario.

Entendamos la serialización y deserialización de Java a través de un código simple:

Primero defina una clase de usuario para la serialización:

public class User implements Serializable{
    
     
    private int age; 
    private String username; 
    private String password; 
    User(){
    
     
        this.age = 10; 
        this.username = "test"; 
        this.password = "test"; 
    } 
    //在序列化之前被调用 
    private void writeObject(ObjectOutputStream os) throws IOException {
    
     
        os.defaultWriteObject(); 
        System.out.println("readObject is running!"); 
    } 
    //在反序列化之后被调用 
    private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
    
     
        is.defaultReadObject(); 
        System.out.println("writeObject is running!"); 
    } 
    @Override 
    public String toString() {
    
     
        return "User{" + "age=" + age + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; 
    } 
}

Luego serializar | deserializar:

public static void main(String args[]) throws IOException, ClassNotFoundException {
    
     
    User user = new User(); //将序列化对象存储在serialize_data中 
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serialize_data")); 
    System.out.println("serialize"); 
    oos.writeObject(user);//序列化 
    oos.close(); //存储在serialize_data中的对象反序列化 
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("serialize_data")); 
    System.out.println("deserialize"); 
    User userDeserialize = (User)ois.readObject();//反序列化 
    System.out.println(userDeserialize.toString()); 
    ois.close(); 
} 
//输出结果 /* serialize readObject is running! deserialize writeObject is running! User{age=10, username='test', password='test'} */

Se puede ver que el método readObject|writeObject personalizado se llama de hecho durante el proceso de serialización, deserialización y deserialización.



1.2 Peligros

La vulnerabilidad de deserialización de Java es muy dañina y puede provocar operaciones de alto riesgo, como la ejecución remota de código y la adquisición de shell.


1.3, uso

  1. Escenario desencadenante de vulnerabilidad

    Java generalmente envía una gran cantidad de objetos serializados entre una aplicación web escrita en Java y un servidor web, como los siguientes escenarios:

    1) Parámetros en solicitudes HTTP, cookies y Parámetros.

    2) Protocolo RMI, el protocolo RMI ampliamente utilizado se basa completamente en la serialización

    3) JMX también se usa para procesar objetos serializados

    4) El protocolo personalizado se utiliza para recibir y enviar el objeto java original

  2. Minería de vulnerabilidad

    (1) Determinar el punto de entrada de deserialización

    La llamada al método readObject se debe encontrar primero y la siguiente operación de inyección se debe realizar después de encontrarla.

    Por lo general, puedes encontrarlo de las siguientes maneras:

    1) Auditoría de código fuente: encuentre el "objetivo" que se puede usar, es decir, determine el sitio de llamada de la función de deserialización readObject.

    2) Capture el comportamiento de la red de la aplicación y busque datos serializados, como wireshark, tcpdump, etc. Nota: los datos serializados de Java generalmente comienzan con una etiqueta (ac ed 00 05), y la característica codificada en base64 es rO0AB.

    (2) Vuelva a comprobar si la biblioteca Apache Commons Collections está incluida en la Class Path de la aplicación.

    (3) Generar payload deserializado

    (4) Envíe nuestros datos de carga útil


1.4 Prevención

Todo programador de Java debe dominar las habilidades de programación para evitar vulnerabilidades de deserialización y cómo reducir el daño causado por bibliotecas peligrosas a las aplicaciones.

  • Una llamada a una clase base peligrosa

Después de descargar el jar, colóquelo en el classpath, reemplace java.io.ObjectInputStream en el código de la aplicación con SerialKiller y luego configúrelo para permitir o deshabilitar algunas clases problemáticas. SerialKiller tiene varias características de Hot-Reload, Whitelisting y Blacklisting. Tipo de confianza después de la deserialización de la entrada externa.

  • Verifique la clase deserializada por Hook resolveClass

Al usar readObject() para deserializar, primero se llamará al método resolveClass para leer el nombre de la clase deserializado, por lo que aquí la verificación de la clase deserializado puede realizarse anulando el método resolveClass del objeto ObjectInputStream.

  • Use ValidatingObjectInputStream para validar clases deserializadas

Utilice el método de aceptación de la clase ValidatingObjectInputStream en el paquete de serialización de Apache Commons IO para implementar el control de lista blanca/negra de la clase de deserialización.Para obtener más información, consulte la introducción de ValidatingObjectInputStream;

  • Use contrast-rO0 para defenderse de los ataques de deserialización

contrast-rO0 es un programa de agente liviano que defiende contra las vulnerabilidades de deserialización al reescribir ObjectInputStream. Use la clase SafeObjectInputStream para implementar el control de lista blanca/lista negra de la clase de deserialización. El código de ejemplo es el siguiente:

SafeObjectInputStream in = new SafeObjectInputStream(inputStream, true);
in.addToWhitelist(SerialObject.class);

in.readObject();
  • Use ObjectInputFilter para validar clases deserializadas

Java 9 incluye nuevas características que admiten el filtrado de datos serializados. Los desarrolladores también pueden heredar la clase java.io.ObjectInputFilter y reescribir el método checkInput para implementar un filtro personalizado, y usar setObjectInputFilter del objeto ObjectInputStream para configurar el filtro para lograr la deserialización. White /Control de lista negra

  • Prohibir que JVM ejecute el comando externo Runtime.exec

Al extender SecurityManager

SecurityManager originalSecurityManager = System.getSecurityManager();
        if (originalSecurityManager == null) {
    
    
            // 创建自己的SecurityManager
            SecurityManager sm = new SecurityManager() {
    
    
                private void check(Permission perm) {
    
    
                    // 禁止exec
                    if (perm instanceof java.io.FilePermission) {
    
    
                        String actions = perm.getActions();
                        if (actions != null && actions.contains("execute")) {
    
    
                            throw new SecurityException("execute denied!");
                        }
                    }
                    // 禁止设置新的SecurityManager,保护自己
                    if (perm instanceof java.lang.RuntimePermission) {
    
    
                        String name = perm.getName();
                        if (name != null && name.contains("setSecurityManager")) {
    
    
                            throw new SecurityException("System.setSecurityManager denied!");
                        }
                    }
                }

                @Override
                public void checkPermission(Permission perm) {
    
    
                    check(perm);
                }

                @Override
                public void checkPermission(Permission perm, Object context) {
    
    
                    check(perm);
                }
            };

            System.setSecurityManager(sm);
        }
  • Lista negra en desuso

Establecer una lista negra de clases durante la deserialización para evitar vulnerabilidades y ataques de deserialización no es un método recomendado para la reparación del código fuente, ya que no puede garantizar que se cubrirán todas las clases posibles, y hay nuevas vulnerabilidades. Cuando sale la carga útil, la lista negra también debe actualizarse, pero en un escenario, la lista negra puede ser una buena opción. Cuando escribo código, siempre encapsulo algunos métodos de uso frecuente en clases públicas, de modo que otros proyectos solo necesiten importar el paquete jar. He visto muchas interfaces públicas que proporcionan operaciones de deserialización antes y usan bibliotecas de terceros. La interfaz de deserialización es no es fácil de arreglar mediante la lista blanca. En este momento, como biblioteca de terceros, no sé quién llamará a la interfaz y qué clases se deserializarán, por lo que en este momento puede usar el método de lista negra para prohibir que se deserialicen algunas clases peligrosas conocidas. Clases específicas de la lista negra, consulte La clase contenida en paylaod en contraste-rO0 e ysoserial.



El próximo blog registrará la idea de crear un entorno de vulnerabilidad de deserialización de Java, algunos códigos importantes y la ejecución de pruebas.



Artículo de referencia

https://www.cnblogs.com/Fluorescence-tjy/p/11222052.html

https://www.cnblogs.com/niceyoo/p/10596657.html

https://www.jianshu.com/p/1c2e8aa874d0

https://www.sohu.com/a/233533386_354899

https://safe.it168.com/a2015/1113/1777/000001777302.shtml

https://www.cnblogs.com/ssooking/p/5875215.html

Supongo que te gusta

Origin blog.csdn.net/m0_47470899/article/details/123798835
Recomendado
Clasificación