点击蓝字 关注我们
点击蓝字 关注我们
遵纪守法
任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得危害网络安全,不得利用网络从事危害国家安全、荣誉和利益
漏洞描述
Cacti是一套基于PHP,MySQL,SNMP及RRDTool开发的网络流量监测图形分析工具。CVE-2022-46169 攻击者可构造恶意请求,在无需登录的情况下访问remote_agent.php 执行任意命令,控制服务器。
风险等级
高
影响版本
1.2.22
漏洞分析
该漏洞存在于remote_agent.php
文件中。无需身份验证即可访问此文件。为了验证客户端是否被允许remote_client_authorized
调用该函数:
if (!remote_client_authorized()) {
print 'FATAL: You are not authorized to use this service';
exit;
}
此函数检索客户端的 IP 地址 viaget_client_addr
并将此 IP 地址解析为相应的主机名 via gethostbyaddr
。此后,验证poller
表中是否存在条目,其中主机名对应于解析的主机名。如果找到这样的条目,函数返回true
并且客户端被授权:
function remote_client_authorized() {
// ...
$client_addr = get_client_addr();
// ...
$client_name = gethostbyaddr($client_addr);
// ...
$pollers = db_fetch_assoc('SELECT * FROM poller', true, $poller_db_cnn_id);
if (cacti_sizeof($pollers)) {
foreach($pollers as $poller) {
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;
}
由于功能的实现,可以绕过此授权get_client_addr
。该函数在文件中定义lib/functions.php
并检查服务$_SERVER
变量以确定客户端的 IP 地址:
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;
foreach ($http_addr_headers as $header) {
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;
}
开头的变量HTTP_
可以由攻击者任意设置。由于poller
表中有一个默认条目,其中包含运行 Cacti 的服务器的主机名,攻击者可以绕过身份验证,例如通过提供标头Forwarded-For: <TARGETIP>
。这样函数get_client_addr
返回运行 Cacti 的服务器的 IP 地址。以下调用gethostbyaddr
会将此 IP 地址解析为服务器的poller
主机名,由于默认条目,该主机名将通过主机名检查。
绕过文件授权后remote_agent.php
,攻击者可以触发不同的动作。其中一项行动称为polldata
:
switch (get_request_var('action')) {
case 'polldata':
// ...
poll_for_data();
// ...
break;
被调用的函数poll_for_data
检索一些请求参数并从数据库中加载相应的poller_item
条目。如果action
apoller_item
等于POLLER_ACTION_SCRIPT_PHP
,则该函数proc_open
用于执行 PHP 脚本:
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, 'local_data_id');
$items = db_fetch_assoc_prepared('SELECT *
FROM poller_item
WHERE host_id = ?
AND local_data_id = ?',
array($host_id, $local_data_id));
// ...
if (cacti_sizeof($items)) {
foreach($items as $item) {
switch ($item['action']) {
// ...
case POLLER_ACTION_SCRIPT_PHP: /* script (php script server) */
// ...
$cactiphp = proc_open(read_config_option('path_php_binary') . ' -q ' . $config['base_path'] . '/script_server.php realtime ' . $poller_id, $cactides, $pipes);
// ...
攻击者控制的参数通过允许任意字符串$poller_id
的函数检索。get_nfilter_request_var
此变量稍后插入到传递给的字符串中proc_open
,这会导致命令注入漏洞。poller_id=;id
例如,通过提供id
命令来执行。
为了到达易受攻击的调用,攻击者必须提供一个host_id
和local_data_id
,其中action
相应poller_item
的设置为POLLER_ACTION_SCRIPT_PHP
。这两个 ID(host_id
和local_data_id
)都可以很容易地被暴力破解。唯一的要求是存在poller_item
带有POLLER_ACTION_SCRIPT_PHP
动作的 a 。这很可能发生在生产实例上,因为此操作是由一些预定义的模板添加的,例如Device - Uptime
或Device - Polling Time
。
poller_item
此命令注入漏洞允许未经身份验证的用户在配置了action
类型POLLER_ACTION_SCRIPT_PHP
( 2
) 的情况下执行任意命令。
应应用以下建议来防止所描述的漏洞。
防护思路
应该通过不允许攻击者使get_client_addr
(file lib/functions.php
) 返回任意 IP 地址来防止授权绕过。这可以通过不遵守HTTP_...
$_SERVER
变量来完成。如果出于兼容性原因应保留这些,至少应防止伪造运行 Cacti 的服务器的 IP 地址。
应通过对文件应用以下更改来防止命令注入remote_agent.php
:
该变量$poller_id
应该是一个整数,因此应该通过函数get_filter_request_var
而不是检索get_nfilter_request_var
:
function poll_for_data() {
// ...
$poller_id = get_filter_request_var('poller_id');
// ...
为了进一步加强对命令注入的防御,$poller_id
应该escapeshellarg
在传递给之前进行转义proc_open
:
function poll_for_data() {
// ...
$cactiphp = proc_open(read_config_option('path_php_binary') . ' -q ' . $config['base_path'] . '/script_server.php realtime ' . escapeshellarg($poller_id), $cactides, $pipes);
// ...
修复建议
1、官方已发布安全更新,建议升级至最新版本。
2、对于在 PHP < 7.0 下运行的 1.2.x 实例,还需要进一步更改, https://github.com/Cacti/cacti/commit/a8d59e8fa5f0054aa9c6981b1cbe30ef0e2a0ec9
另外关注公众号后台回复“框架RCE”可获取常见框架漏洞利用工具,后台回复“pentest”获取常用渗透测试工具集。回复“apk11”获取apk测试工具集。
关注公众号
下面就是团队的公众号啦,更新的文章都会在第一时间推送在公众号
63篇原创内容
支持作者
原文始发于微信公众号(CKCsec安全研究院):CVE-2022-46169:Cacti命令注入漏洞