CVE-2023-30534: deserialización no segura en las versiones de Cacti anteriores a la 1.2.25
Descripción general
Hemos descubierto dos instancias de deserialización no segura en las versiones de Cacti anteriores a la 1.2.25 que hemos identificado como CVE-2023-30534. Estas instancias se pueden activar a distancia, pero no es posible aprovecharse de ellas debido a un entorno de ejecución limitado que se describe en el apartado «Intentos de uso malicioso».
Por si alguien no lo sabe, Cacti es una solución de gestión de redes y representaciones visuales de código abierto que utiliza el almacenamiento de datos y las funcionalidades de RRDtool para descubrir dispositivos y crear gráficas, entre otras tareas. Aunque existe la posibilidad de acceder a algunas instancias de Cacti de forma externa, esta herramienta se suele emplear para supervisar sistemas de red internos.
Profundicemos un poco en los aspectos técnicos. La serialización es el proceso que permite transformar estructuras de datos complejas, como objetos de PHP o Java, a un formato más fácil de enviar a través de una red, y la deserialización es justo lo contrario. Cuando los datos controlados por el usuario se deserializan de tal forma que posibles atacantes pueden crear instancias de objetos aleatorios, leer y escribir archivos e incluso ejecutar código a distancia, hablamos de una deserialización no segura.
Nota: las versiones 1.2.25 y 1.3.0 de Cacti eliminan otras 17 vulnerabilidades, algunas de ellas críticas y graves, por lo que los administradores deben actualizarlo cuanto antes.
Versiones afectadas
La vulnerabilidad de deserialización no segura se descubrió en Cacti 1.2.10 y afecta a todas las versiones anteriores a la 1.2.25. La versión 1.3.0, lanzada al mismo tiempo que la 1.2.5, también arregla este problema.
Desglose de las vulnerabilidades de deserialización no segura
Todas las instancias de deserialización no segura están relacionadas con el uso de la función unserialize
sin corregir correctamente la entrada del usuario. Aunque Cacti dispone de una utilidad de deserialización segura que intenta corregir el contenido de una cadena y comprobar que solo incluye valores específicos antes de deserializarlo, no se aplicó a estas instancias.
Nota: en los bloques de código que aparecen a continuación, los puntos suspensivos representan los fragmentos que se han omitido por ser irrelevantes.
managers.php
El código vulnerable se encuentra en el archivo managers.php
y, más concretamente, en la función «form_actions». El siguiente fragmento de código pertenece a Cacti 1.2.10:
function form_actions() { global $manager_actions, $manager_notification_actions; if (isset_request_var('selected_items')) { if (isset_request_var('action_receivers')) { ... ... ... } elseif (isset_request_var('action_receiver_notifications')) { get_filter_request_var('id'); $selected_items = unserialize(stripslashes(get_nfilter_request_var('selected_items'))); ... } ...}
Al establecer la variable de petición POST action_receiver_notifications
, podemos dirigir el flujo de código hacia la llamada unserialize
de la función form_actions
. Para activar form_actions
, tenemos que cambiar la variable action a actions. De este modo, se activará el caso correspondiente en la declaración condicional, como se muestra a continuación:
switch (get_request_var('action')) { case 'save': form_save(); break; case 'actions': form_actions(); break; ... ...
Para llegar a la función vulnerable unserialize
, un usuario autenticado debe enviar una petición POST que incluya un objeto PHP serializado con codificación URL a la variable «selected_items», como ocurre aquí:
Si elegimos un objeto no válido (por ejemplo, que esté mal formado o incluya una clase desconocida para Cacti) como valor de la variable «selected_items», Cacti no podrá deserializar la carga útil, tal y como se aprecia en los registros:
Llegados a este punto, podemos hacer que el flujo de código haga una llamada a «unserialize» en un objeto serializado y controlado por el usuario de managers.php.
graphs_new.php
El código vulnerable se encuentra en el archivo graphs_new.php
y, más concretamente, en la función host_new_graphs_save
. El siguiente fragmento de código pertenece a Cacti 1.2.10:
function host_new_graphs_save($host_id) { $selected_graphs_array = unserialize(stripslashes(get_nfilter_request_var('selected_graphs_array')));
$values = array();
Esta función deserializa el parámetro controlado por el usuario selected_graphs_array
. stripslashes
se ejecuta primero, pero solo se encarga de eliminar las barras oblicuas que hacen falta en las cadenas durante el preprocesamiento (por ejemplo, \” se convierte en ”).
Para llegar a esta función, el usuario envía una petición a graphs_new.php
con action=save
y save_component_new_graphs=1
. Estos dos parámetros llaman a la función vulnerable, como se muestra en el siguiente fragmento:
switch (get_request_var('action')) { case 'save': form_save(); break; case 'query_reload': ... ... ...function form_save() { ... ... ... if (isset_request_var('save_component_new_graphs')) { host_new_graphs_save(get_filter_request_var('host_id'));
header('Location: graphs_new.php?host_id=' . get_filter_request_var('host_id') . &header=false'); }}
Para llegar a la función vulnerable unserialize
, un usuario autenticado debe enviar una petición POST que incluya un objeto PHP serializado con codificación URL a la variable selected_graphs_array
, como ocurre aquí:
Si elegimos un objeto no válido (por ejemplo, que esté mal formado o incluya una clase desconocida para Cacti) como valor de la variable selected_graphs_array
, Cacti intentará deserializar la carga útil, tal y como se aprecia en los registros:
Llegados a este punto, podemos hacer que el flujo de código haga una llamada a «unserialize» en un objeto serializado y controlado por el usuario de graphs_new.php
.
Intentos de uso malicioso
Métodos mágicos PHP
Para aprovecharse de una vulnerabilidad de deserialización de PHP hay que acceder a objetos que tengan implementados métodos mágicos. Las llamadas a métodos mágicos se realizan automáticamente cuando un objeto se crea o se destruye, entre otras situaciones. Algunos de estos métodos son __construct
, __unserialize
, __destruct
y __wake
. Necesitamos acceder a objetos que usen estas funciones porque se llamará automáticamente a algunas de ellas cuando la carga útil se deserialice y se convierta en un objeto. En este contexto, un «gadget» es un objeto que tiene implementado uno de estos métodos y que podemos usar en una carga útil. Al utilizar objetos anidados en las cargas útiles, podemos juntar estos objetos y crear una «cadena de gadgets» que lleve a cabo una acción específica, como leer un archivo, escribir un archivo o ejecutar código.
Búsqueda de cadenas de gadgets
Con el tiempo, se han ido descubriendo muchas cadenas de gadgets en las bibliotecas de uso más extendido. La herramienta PHPGCC contiene una lista de cadenas de gadgets conocidas que puede utilizar en la creación de cargas útiles para las vulnerabilidades de deserialización no segura. Hay una cadena de gadgets conocida en los proveedores de PHP de Cacti: PHPSecLib. En cualquier caso, Cacti no requiere ni incluye ninguno de los gadgets necesarios para usar la cadena PHPSecLib en una carga útil (es decir, TripleDES, AES y SSH1).
También existe la posibilidad de crear nuevos gadgets si el proyecto contiene objetos con métodos mágicos utilizables. Por ejemplo, puedes echar un vistazo al código fuente de las implementaciones del método «__construct» de Cacti para saber si se puede utilizar alguna. Tras analizar los métodos mágicos implementados en los objetos PHP de Cacti, hemos llegado a la conclusión de que ninguno se puede añadir a una nueva cadena
y que, por tanto, no es posible aprovecharse de la vulnerabilidad.
Plantilla Nuclei para CVE-2023-30534
Hemos creado una plantilla Nuclei para CVE-2023-30534 con el objetivo de agilizar las pruebas de la vulnerabilidad y las posteriores correcciones. Esta plantilla requiere autenticación y envía un objeto PHP no válido para generar un mensaje de registro que muestra la llamada a «unserialize» en el objeto. A continuación, verifica que se ha llegado a la llamada buscando el mensaje de error en los registros y comprobando la llamada a la función «unserialize» en la carga útil. Aquí hay un ejemplo de su ejecución en un entorno local:
Los administradores pueden poner fin a esta vulnerabilidad actualizando a Cacti 1.2.5 o una versión posterior, como la 1.3.0. Aunque no se puede hacer un uso malicioso de esta vulnerabilidad en una instalación estándar, conviene que los usuarios sigan las advertencias de seguridad de Cacti.
Nota: las versiones 1.2.25 y 1.3.0 de Cacti eliminan otras 17 vulnerabilidades, algunas de ellas críticas y graves, por lo que los administradores deben actualizarlo cuanto antes.