CVE-2023-30534: Insecure Deserialization in Cacti prior to 1.2.25

Overview 

We discovered two instances of insecure deserialization in Cacti versions prior to 1.2.25, tracked as CVE-2023-30534. Each instance of insecure deserialization can be triggered remotely, but cannot be exploited due to a limited execution environment which is discussed further in the "Attempting Exploitation" section.

For those unfamiliar, Cacti is an open source network management and graphing solution that utilizes RRDtool’s data storage and graphing functionality to discover devices, create graphs, and more. While some instances of Cacti are accessible externally, its primary use is for monitoring internal network systems.

Now, to dive a bit deeper into the technical aspect: Serialization is the process of taking complex data structures like PHP or Java objects and transforming them into a format that is easier to send over the network. Deserialization is when that data gets transformed back into a complex data structure. An insecure deserialization occurs when user-controlled data is deserialized, potentially enabling attackers to instantiate arbitrary objects, read and write files, and even gain remote code execution.

Note: Cacti version 1.2.25 (and 1.3.0) fixes an additional 17 vulnerabilities in Cacti, including critical and high-severity vulnerabilities, so administrators should upgrade when possible.

Affected versions

We originally discovered the insecure deserialization vulnerability in Cacti 1.2.10, and all versions prior to 1.2.25 are vulnerable. Cacti version 1.3.0, released alongside 1.2.5, also fixes the insecure deserialization.

Breaking down the insecure deserialization vulnerabilities

Each instance of insecure deserialization is tied to the usage of the unserialize function without properly sanitizing user input. While Cacti does offer a “safe deserialization” utility function that attempts to sanitize and verify that the string content only contains specific values before calling unserialize, this function was not used in these instances.

Note: In code blocks, … represents unreferenced code that has been removed for brevity.

managers.php

The vulnerable code is located in the managers.php file, specifically within the form_actions function. The following code snippet is from Cacti version 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')));
...
}
...
}

By setting the POST request variable action_receiver_notifications, we can direct the code flow to hit the unserialize call in the form_actions function. To trigger form_actions, we need to set the “action” variable to “actions”. This ensures we hit the proper case in the switch statement, as shown below:

switch (get_request_var('action')) {
case 'save':
form_save();
break;
case 'actions':
form_actions();
break;
...
...

To target the vulnerable unserialize function, an authenticated user sends a POST request that includes a URL encoded serialized PHP object in the “selected_items” variable, as follows:

cve blog image 1

If we put an invalid object (e.g. one that is malformed or includes a class Cacti does not know about) as the value for the “selected_items” variable, we can see Cacti failing to unserialize the payload in the logs:

cve blog image 2

At this point, we can control code flow to call unserialize on a user-controlled, serialized object in managers.php.

graphs_new.php

The vulnerable code is located in the graphs_new.php file, specifically in the host_new_graphs_save function. The following code snippet is from Cacti version 1.2.10:

function host_new_graphs_save($host_id) {
$selected_graphs_array = unserialize(stripslashes(get_nfilter_request_var('selected_graphs_array')));
$values = array();

This function unserializes the user controlled parameter selected_graphs_array. While stripslashes is run first, this simply removes extra slashes that are needed in strings during pre-processing (e.g. \” to “). 

In order to reach this function, the user makes a request to graphs_new.php with two parameters: action=save and save_component_new_graphs=1, which causes the vulnerable function to be called, as shown in the code snippet below:

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');
}
}

To target the vulnerable unserialize function, an authenticated user sends a POST request that includes a URL encoded serialized PHP object in the selected_graphs_array variable, as follows:

cve blog image 3

If we put an invalid object (e.g. one that is malformed or includes a class Cacti does not know about) as the value for the selected_graphs_array variable, we can see Cacti attempting to unserialize the payload in the logs:

cve blog image 4

At this point, we can control code flow to call unserialize on a user-controlled, serialized object in graphs_new.php.

Attempting Exploitation

PHP Magic Methods

Exploiting a PHP deserialization vulnerability requires access to Objects that have “magic methods” implemented.  Magic methods are called on objects automatically as they are created, destroyed, and in other situations. Examples of these methods include __construct, __unserialize, __destruct, __wake, and others. We need access to objects that use these functions because some of them will be automatically called when our payload is unserialized into an Object. In this context, a “gadget” is an object with one of these methods implemented that we can use in a payload. By using nested objects in payloads, we combine these gadgets together into a “gadget chain” to perform a desired action such as reading a file, writing a file, or gaining code execution. 

Searching for Gadget Chains

Over time, many gadget chains have been discovered in popular libraries. The PHPGCC tool has a list of known gadget chains that it can use to create payloads for insecure deserialization vulnerabilities. There is one known usable gadget chain in Cacti’s PHP vendors, PHPSecLib. However, Cacti does not require or include any of the necessary gadgets that are needed (TripleDES, AES, and SSH1) in order to successfully use the PHPSecLib gadget chain in a payload.

In addition to making use of existing gadget chains, it’s possible to create new ones if the project has objects with usable magic methods. For example, you can look at cacti’s __construct method implementations by viewing the source code to see if some of those are usable. After looking through the implemented magic methods in Cacti’s PHP objects, none were found to be usable in a new gadget chain.

This leaves the vulnerability not exploitable, because of the inability to find a usable gadget chain.

Nuclei Template for CVE-2023-30534

In order to facilitate quick testing for the vulnerability, as well as to aid remediation efforts, we created a nuclei template that tests for CVE-2023-30534. This template requires authentication and submits an invalid PHP object in order to generate a log message that shows unserialize being called on the object. The template then verifies the unserialize call was reached by checking for the error message in the logs, ensuring the unserialize function was called on our payload. The template being run locally is shown below:

cve blog image 4

Cacti administrators can remediate the vulnerability by upgrading to version 1.2.5 or later (e.g., 1.3.0). This vulnerability cannot be exploited in a standard Cacti installation, but users should follow the advice provided by Cacti in the security advisory for further details on remediation.

Note: Cacti version 1.2.25 (and 1.3.0) fixes an additional 17 vulnerabilities in Cacti, including critical and high-severity vulnerabilities, so administrators should upgrade as soon as possible.

Published

5 min read

Want to continue the conversation?
Schedule time with an expert
Share this post

The Fastly Security Research Team focuses on ensuring our customers have the tools and data available to them to keep their systems secure. They analyze and ultimately help prevent attacks at Fastly scale. The team is a group of behind-the-scenes security experts who are here to help you stay on the cutting edge of the ever-evolving security landscape.

Matthew Mathur
Senior Security Researcher

Matthew is a Senior Security Researcher at Fastly, focusing on vulnerability research, web application attacks, and developing protections. Matthew is an active contributor to several open source security tools including the Metasploit Framework and Nuclei, and is passionate about sharing research with the security community.

Ready to get started?

Get in touch or create an account.