Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2023-39361 — 针对 CVE-2023-39361 的分步分析和实验环境搭建,该漏洞是 Cacti v1.2.24 中的未认证 SQL 注入,附带利用与缓解措施的演练。 | Kitploit
工具/GitHubGitHub/hpt-intern-task-submission/cve-2023-39361
漏洞分析漏洞利用Web应用程序漏洞利用学习与教育实验室与实践
GitHubhpt-intern-task-submission/cve-2023-39361

CVE-2023-39361

针对 CVE-2023-39361 的分步分析和实验环境搭建,该漏洞是 Cacti v1.2.24 中的未认证 SQL 注入,附带利用与缓解措施的演练。

查看仓库

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
102年前尚未审核
分享

[CVE-2023-39361] Cacti v1.2.24 中的未经身份验证的 SQL 注入

概述

Cacti 是一款开源的运维监控工具,使用 PHP、MySQL/MariaDB 编写,提供友好的界面。该漏洞于 2023 年被发现,影响 1.2.24 之前的所有版本。此安全缺陷在于向 SQL 查询中插入值时实现不当。这是一个严重的 SQL 注入漏洞,允许攻击者修改数据库以及远程执行代码。

实验环境搭建:

在本分析中,为简单起见,我将在 Docker 中运行 Cacti。首先,我们创建如下所示的 docker-compose.yml 文件,并运行命令 docker-compose up -d。

version: '3.5'

services:

  

  

cacti:

image: "smcline06/cacti"

container_name: CVE-2023-39361

domainname: example.com

hostname: cacti

ports:

- "80:80"

- "443:443"

environment:

- DB_NAME=cacti_master

- DB_USER=cactiuser

- DB_PASS=cactipassword

- DB_HOST=db

- DB_PORT=3306

- DB_ROOT_PASS=rootpassword

- INITIALIZE_DB=1

- TZ=America/Los_Angeles

volumes:

- cacti-data:/cacti

- cacti-spine:/spine

- cacti-backups:/backups

links:

- db

  
  

db:

image: "mariadb:10.3"

container_name: CVE-2023-39361_db

domainname: example.com

hostname: db

ports:

- "3306:3306"

command:

- mysqld

- --character-set-server=utf8mb4

- --collation-server=utf8mb4_unicode_ci

- --max_connections=200

- --max_heap_table_size=128M

- --max_allowed_packet=32M

- --tmp_table_size=128M

- --join_buffer_size=128M

- --innodb_buffer_pool_size=1G

- --innodb_doublewrite=ON

- --innodb_flush_log_at_timeout=3

- --innodb_read_io_threads=32

- --innodb_write_io_threads=16

- --innodb_buffer_pool_instances=9

- --innodb_file_format=Barracuda

- --innodb_large_prefix=1

- --innodb_io_capacity=5000

- --innodb_io_capacity_max=10000

environment:

- MYSQL_ROOT_PASSWORD=rootpassword

- TZ=America/Los_Angeles

volumes:

- cacti-db:/var/lib/mysql

  
  

volumes:

cacti-db:

cacti-data:

cacti-spine:

cacti-backups:

构建容器后,我们可以通过访问 http://localhost:80 来访问 Cacti。下一步是将 Cacti 更新到 1.2.24 版本。你可以在此处下载升级脚本:https://pastebin.com/NfRiHLjR。将文件保存到与 docker-compose.yml 相同的目录,并将其重命名为 upgrade_cacti.sh。然后运行以下两个命令:

docker cp upgrade_cacti.sh CVE-2023-39361:/tmp/upgrade_cacti.sh && docker exec -it CVE-2023-39361 bash -c "bash /tmp/upgrade_cacti.sh"

现在我们可以开始了,让我们深入研究代码,看看是什么导致了 SQL 注入攻击。存在漏洞的文件是 graph_view.php,我们只需要 guest 用户即可访问此文件,从而允许任何人利用此漏洞。在该文件中,不安全的函数是 grow_right_pane_tree()。

grow_right_pane_tree

在深入分析此函数之前,我们需要回溯了解该函数是如何被执行的。

<?php
switch (get_nfilter_request_var('action')) {
//.... 
// Many other cases
case  'tree_content':
	//..... Some code here
	// ---------
	if (isset_request_var('node')) {
		$parts = explode('-', sanitize_search_string(get_request_var('node')));
		// Check for tree anchor
		if (strpos(get_nfilter_request_var('node'), 'tree_anchor') !== false) {
			$tree_id = $parts[1];
			$node_id = 0;
		} 
	//..... Some code here

	if ($tree_id > 0) {
		if (!is_tree_allowed($tree_id)) {
			header('Location: permission_denied.php');
			exit;
		}
		// Vulnerable function is called here
		grow_right_pane_tree($tree_id, $node_id, $hgdata);
	}

从上面的代码片段可以看出,当 $tree_id > 0 时,该函数将会被执行。所有步骤可以解释如下:

  • 首先,程序从用户输入中获取 action 参数的值,并进入一个 switch/case 语句。

  • 在 tree_content 分支中,代码从用户处获取请求参数 node。接着,它通过 - 字符分割输入,保存到 $part,最后检查 node 中是否包含 tree_anchor。如果所有条件都匹配,$tree_id 将被赋值为 $part 中的第二个元素。例如,tree_content-1 或 1-2-tree_content 都是有效的,$tree_id 的值将相应地设定。

至此,我们的 URL 看起来像这样:http://localhost:80?action=tree_content&node=tree_anchor-1。现在是时候分析 grow_right_pane_tree() 函数来利用这个漏洞了。

grow_right_pane_tree_analyze

易受攻击的参数是 rfilter,它被直接传递到 WHERE 子句中的 RLIKE 操作数中。使其易受攻击的原因是 rfilter 被双引号 " 包围,但该参数由 html_validate_tree_vars() 函数检查,该函数如下所示:

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');
	/* ================= input validation ================= */
	// ........
}

rfilter 的过滤器类型设置为 FILTER_VALIDATE_IS_REGEX,并存储在 $filters 中,然后 $filter 被传递到 validate_store_request_vars() 函数中。让我们深入了解这个函数,这将揭示我们分析的其余部分:

function validate_store_request_vars($filters, $sess_prefix = '') {
	// .......
	if (cacti_sizeof($filters)) {
		foreach($filters as $variable => $options) {
		// ....
		elseif ($options['filter'] == FILTER_VALIDATE_IS_REGEX) {
					if (is_base64_encoded($_REQUEST[$variable])) {
						$_REQUEST[$variable] = base64_decode($_REQUEST[$variable]);
					}
					$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;
	}

validate_store_request_vars() 函数负责验证用户输入。当 filter 类型为 FILTER_VALIDATE_IS_REGEX 时,该函数调用 validate_is_regex() 函数,该函数使用 preg_match 来验证我们的输入是否有效。根据 PHP 文档:

preg_match() 如果 pattern 匹配给定的 subject,则返回 1;如果不匹配,则返回 0;如果失败,则返回 false。

if 语句使用了严格比较,这意味着只有在发生错误时,我们才无法进入该 if 语句。$regex(即我们的输入)被包裹在两个单引号 ' 之间。开发者的意图是防止用户注入通常用于发起 SQL 注入 攻击的单引号 '。这看起来是一个健壮的安全实现,但让我们再看一下 rfilter 是如何被传递到 WHERE 子句中的:

$sql_where  .=  ' (gtg.title_cache RLIKE "'  .  get_request_var('rfilter') .  '" OR gtg.title RLIKE "'  .  get_request_var('rfilter') .  '")';

啊哦,验证用户输入的努力变得毫无意义,因为 rfilter 被包裹在双引号 " 内,这允许攻击者轻松地注入 " 来跳出该语句。

理解了为什么它易受攻击之后,现在我们将深入研究 $sql_where 被传递到的查询。

grow_right_pane_tree_analyze

在 $sql_where 被赋值之后,程序将调用 get_allowed_tree_header_graphs() 函数。该函数如下所示:

function  get_allowed_tree_header_graphs($tree_id, $leaf_id = 0, $sql_where = '', $sql_order = 'gti.position', $sql_limit = '', &$total_rows = 0, $user_id = 0) {
	// ......
	if ($sql_where != '') {
		$sql_where = " AND ($sql_where)";
}
	$sql_where = "WHERE (gti.graph_tree_id=$tree_id AND gti.parent=$leaf_id)" . $sql_where;
	
	$graphs = db_fetch_assoc("SELECT gti.id, gti.title, gtg.local_graph_id, h.description, gt.name AS template_name, gtg.title_cache, gtg.width, gtg.height, gl.snmp_index, gl.snmp_query_id
	FROM graph_templates_graph AS gtg
	INNER JOIN graph_local AS gl
	ON gl.id = gtg.local_graph_id
	INNER JOIN graph_tree_items AS gti
	ON gti.local_graph_id = gl.id
	LEFT JOIN graph_templates AS gt
	ON gt.id = gl.graph_template_id
	LEFT JOIN host AS h
	ON h.id = gl.host_id
	$sql_where
	$sql_order
	$sql_limit");
下载工具