
Un analizador de código de bytes para encontrar cadenas de gadgets de deserialización en aplicaciones Java
Este proyecto inspecciona bibliotecas Java y classpaths en busca de cadenas de gadgets. Las cadenas de gadgets se utilizan para construir exploits para vulnerabilidades de deserialización. Al descubrir automáticamente posibles cadenas de gadgets en el classpath de una aplicación, los testers de penetración pueden construir exploits rápidamente y los ingenieros de seguridad de aplicaciones pueden evaluar el impacto de una vulnerabilidad de deserialización y priorizar su remediación.
Este proyecto fue presentado en Black Hat USA 2018. ¡Aprende más al respecto allí! (Enlaces pendientes)
AVISO: Este proyecto está en fase alfa en el mejor de los casos. Necesita pruebas y documentación añadidas. ¡Siéntete libre de ayudar añadiendo cualquiera de ellas!
Suponiendo que tienes un JDK instalado en tu sistema, deberías poder simplemente ejecutar ./gradlew shadowJar. Luego puedes ejecutar la aplicación con java -jar build/libs/gadget-inspector-all.jar <args>.
Esta aplicación espera como argumento(s) ya sea una ruta a un archivo war (en cuyo caso el war será descomprimido y todas sus clases y bibliotecas se usarán como classpath) o cualquier número de jars.
Ten en cuenta que el análisis puede consumir mucha memoria (y hasta ahora gadget inspector no ha sido optimizado en absoluto para ser menos voraz en memoria). Para bibliotecas pequeñas probablemente quieras asignar al menos 2 GB de tamaño de heap (es decir, con la bandera -Xmx2G). Para aplicaciones más grandes querrás usar tanta memoria como puedas permitirte.
El kit de herramientas pasará por varias etapas de inspección del classpath para construir conjuntos de datos para usar en etapas posteriores. Estos conjuntos de datos se escriben en archivos con extensión .dat y pueden descartarse después de tu ejecución (se escriben principalmente para que las etapas anteriores puedan omitirse durante el desarrollo).
Después de que el análisis se haya ejecutado, se escribirá el archivo gadget-chains.txt.
El siguiente es un ejemplo de ejecución contra commons-collections-3.2.1.jar, por ejemplo con
wget http://central.maven.org/maven2/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar
java -Xmx2G -jar build/libs/gadget-inspector-all.jar commons-collections-3.2.1.jar
En gadget-chains.txt se encuentra la siguiente cadena:
com/sun/corba/se/spi/orbutil/proxy/CompositeInvocationHandlerImpl.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; (-1)
com/sun/corba/se/spi/orbutil/proxy/CompositeInvocationHandlerImpl.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; (0)
org/apache/commons/collections/map/DefaultedMap.get(Ljava/lang/Object;)Ljava/lang/Object; (0)
org/apache/commons/collections/functors/InvokerTransformer.transform(Ljava/lang/Object;)Ljava/lang/Object; (0)
java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (0)
El punto de entrada de esta cadena es una implementación de la clase InvocationHandler del JDK. Utilizando el mismo truco que en la cadena de gadgets original de commons-collections, cualquier implementación serializable de esta clase es alcanzable en una cadena de gadgets, por lo que la cadena descubierta comienza aquí. Este método invoca classToInvocationHandler.get(). La cadena de gadgets descubierta indica que classToInvocationHandler puede ser serializado como un DefaultedMap de modo que esta invocación salta a DefaultedMap.get(). El siguiente paso en la cadena invoca value.transform() desde este método. El parámetro value en esta clase puede ser serializado como un InvokerTransformer. Dentro del método transform de esta clase, vemos que llamamos a cls.getMethodName(iMethodName, ...).invoke(...). Gadget inspector determinó que iMethodName es controlable por el atacante como miembro serializado, y por lo tanto un atacante puede ejecutar un método arbitrario en la clase.
Esta cadena de gadgets es el bloque de construcción de la cadena de gadgets completa de commons-collections descubierta por Frohoff. En el caso anterior, el gadget inspector descubrió la entrada a través de CompositeInvocationHandlerImpl y DefaultedMap en lugar de AnnotationInvocationHandler y LazyMap, pero es en gran medida la misma.
Si buscas más ejemplos de qué tipo de cadenas puede encontrar esta herramienta, las siguientes bibliotecas también tienen algunos resultados interesantes:
No olvides que también puedes apuntar gadget inspector a una aplicación completa (empaquetada como JAR o WAR). Por ejemplo, al analizar el war de la aplicación Zksample2 obtenemos la siguiente cadena de gadgets:
net/sf/jasperreports/charts/design/JRDesignPieDataset.readObject(Ljava/io/ObjectInputStream;)V (1)
org/apache/commons/collections/FastArrayList.add(Ljava/lang/Object;)Z (0)
java/util/ArrayList.clone()Ljava/lang/Object; (0)
org/jfree/data/KeyToGroupMap.clone()Ljava/lang/Object; (0)
org/jfree/data/KeyToGroupMap.clone(Ljava/lang/Object;)Ljava/lang/Object; (0)
java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (0)
Como puedes ver, esto utiliza varias bibliotecas diferentes contenidas en la aplicación para construir la cadena.
P: Si gadget inspector encuentra una cadena de gadgets, ¿se puede construir un exploit a partir de ella?
R: No siempre. El análisis utiliza algunas suposiciones simplificadoras y puede reportar falsos positivos (cadenas de gadgets que en realidad no existen). Como ejemplo simple, no intenta resolver la satisfacibilidad de las condiciones de ramificación. Por lo tanto, reportará lo siguiente como una cadena de gadgets:
public class MySerializableClass implements Serializable {
public void readObject(ObjectInputStream ois) {
if (false) System.exit(0);
ois.defaultReadObject();
}
}
Además, gadget inspector tiene condiciones bastante amplias sobre aquellas funciones que considera interesantes. Por ejemplo, trata la reflexión como interesante (es decir, llamadas a Method.invoke() donde un atacante puede controlar el método), pero a menudo las aserciones pasadas por alto significan que un atacante puede influir en el método invocado pero no tiene control completo. Por ejemplo, un atacante puede ser capaz de invocar el método "getError()" en cualquier clase, pero no cualquier otro nombre de método.
P: Si no se encontraron cadenas de gadgets, ¿significa eso que mi aplicación está a salvo de explotación?
R: ¡No! Por un lado, gadget inspector tiene un conjunto muy limitado de funciones "sumidero" que considera que tienen efectos secundarios "interesantes". Esto ciertamente no significa que no haya otros comportamientos interesantes o peligrosos que no estén en la lista.
Además, hay una serie de limitaciones en el análisis estático que significan que gadget inspector siempre tendrá puntos ciegos. Como ejemplo, gadget inspector actualmente pasaría por alto esto porque no sigue las llamadas de reflexión.
public class MySerializableClass implements Serializable {
public void readObject(ObjectInputStream ois) {
System.class.getMethod("exit", int.class).invoke(null, 0);
}
}