Skip to content

Commit

Permalink
Merge pull request from GHSA-6p93-p743-35gf
Browse files Browse the repository at this point in the history
* Resolving  CVE-2022-46169

* Provide a restrictive option

* QA: Further restrict allowed headers to those defined in config only

Co-authored-by: TheWitness <thewitness@cacti.net>
  • Loading branch information
netniV and TheWitness committed Dec 5, 2022
1 parent c9ca964 commit b43f13a
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Cacti CHANGELOG

1.2.23
-security#4920: Add .htaccess file to scripts folder
-security#XXXX: CVE-2022-46169 Unauthenticated Command Injection in Remote Agent
-issue#4418: When using Single Signon Frameworks like SiteMinder Cacti does not properly detect ticket revocation in callbacks
-issue#4682: New templates are not installed during the update
-issue#4838: CLI Based upgrade generates warnings in PHP 8.1
Expand Down
6 changes: 3 additions & 3 deletions auth_login.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
cacti_log("LOGIN: Guest User '" . $user['username'] . "' in use", false, 'AUTH');
}

$client_addr = get_client_addr('');
$client_addr = get_client_addr();

db_execute_prepared(
'INSERT IGNORE INTO user_log
Expand Down Expand Up @@ -269,10 +269,10 @@
'INSERT IGNORE INTO user_log
(username, user_id, result, ip, time)
VALUES (?, ?, 0, ?, NOW())',
array($username, !empty($id) ? $id : 0, get_client_addr(''))
array($username, !empty($id) ? $id : 0, get_client_addr())
);

cacti_log('LOGIN FAILED: ' . $realm_name . " Login Failed for user '" . $username . "' from IP Address '" . get_client_addr('') . "'.", false, 'AUTH');
cacti_log('LOGIN FAILED: ' . $realm_name . " Login Failed for user '" . $username . "' from IP Address '" . get_client_addr() . "'.", false, 'AUTH');
}
}

Expand Down
26 changes: 26 additions & 0 deletions include/config.php.dist
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,29 @@ $disable_log_rotation = false;
* are defined in lib/database.php
*/
# define('DEBUG_SQL_FLOW', true);

/*
* Allow the use of Proxy IPs when searching for client
* IP to be used
*
* This can be set to one of the following:
* - false: to use only REMOTE_ADDR
* - true: to use all allowed headers (not advised)
* - array of one or more the following:
* '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',
*
* NOTE: The following will always be checked:
* 'REMOTE_ADDR',
*/
$proxy_headers = null;
3 changes: 3 additions & 0 deletions include/global.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@
exit;
}

/* Should we allow proxy ip headers? */
$config['proxy_headers'] = $proxy_headers ?? null;

/* Set the poller_id */
if (isset($poller_id)) {
$config['poller_id'] = $poller_id;
Expand Down
16 changes: 16 additions & 0 deletions include/global_arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -2931,4 +2931,20 @@
);
}

$allowed_proxy_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',
);

api_plugin_hook('config_arrays');
16 changes: 8 additions & 8 deletions lib/auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ function set_auth_cookie($user) {
if (db_table_exists('user_auth_cache')) {
clear_auth_cookie();

$nssecret = md5($_SERVER['REQUEST_TIME'] . mt_rand(10000,10000000)) . md5(get_client_addr(''));
$nssecret = md5($_SERVER['REQUEST_TIME'] . mt_rand(10000,10000000)) . md5(get_client_addr());

$secret = hash('sha512', $nssecret, false);

db_execute_prepared('REPLACE INTO user_auth_cache
(user_id, hostname, last_update, token)
VALUES
(?, ?, NOW(), ?);',
array($user['id'], get_client_addr(''), $secret));
array($user['id'], get_client_addr(), $secret));

cacti_cookie_session_set($user['id'], $user['realm'], $nssecret);
}
Expand Down Expand Up @@ -161,7 +161,7 @@ function check_auth_cookie() {
(username, user_id, result, ip, time)
VALUES
(?, ?, 2, ?, NOW())',
array($user_info['username'], $user_info['id'], get_client_addr(''))
array($user_info['username'], $user_info['id'], get_client_addr())
);

return $user_info['id'];
Expand Down Expand Up @@ -3430,7 +3430,7 @@ function auth_process_lockout($username, $realm) {

if (cacti_sizeof($user)) {
if ($user['enabled'] == '') {
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr('') . "'. User account Disabled.", false, 'AUTH');
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr() . "'. User account Disabled.", false, 'AUTH');

$error = true;
$error_msg = __('Access Denied! Login Disabled.');
Expand Down Expand Up @@ -3464,22 +3464,22 @@ function auth_process_lockout($username, $realm) {
db_execute_prepared('INSERT IGNORE INTO user_log
(username, user_id, result, ip, time)
VALUES (?, ?, 0, ?, NOW())',
array($username, isset($user['id']) ? $user['id']:0, get_client_addr('')));
array($username, isset($user['id']) ? $user['id']:0, get_client_addr()));

if ($user['locked'] == 'on') {
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr('') . "'. Account is locked out.", false, 'AUTH');
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr() . "'. Account is locked out.", false, 'AUTH');

$error = true;
$error_msg = __('Your account has been locked. Please contact your Administrator.');
} else {
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr('') . "'.", false, 'AUTH');
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr() . "'.", false, 'AUTH');

/* error */
$error = true;
$error_msg = __('Access Denied! Login Failed.');
}
} elseif ($user['locked'] == 'on') {
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr('') . "'.", false, 'AUTH');
cacti_log("LOGIN FAILED: Local Login Failed for user '" . $username . "' from IP Address '" . get_client_addr() . "'.", false, 'AUTH');

$error = true;
$error_msg = __('Access Denied! Login Failed.');
Expand Down
69 changes: 41 additions & 28 deletions lib/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -7079,37 +7079,50 @@ function get_debug_prefix() {
return sprintf('<[ %s | %7d ]> -- ', $dateTime, getmypid());
}

function get_client_addr($client_addr = false, $restrictive = false) {
if (!$restrictive) {
$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',
);
} else {
$http_addr_headers = array(
'X-Forwarded-For',
'X-Client-IP',
'X-Real-IP',
'X-ProxyUser-Ip',
'CF-Connecting-IP',
'True-Client-IP',
'REMOTE_ADDR',
);
function get_client_addr() {
global $config, $allowed_proxy_headers;

$proxy_headers = $config['proxy_headers'] ?? null;
if ($proxy_headers === null) {
$last_time = read_config_option('proxy_alert');
if (empty($last_time)) {
$last_time = date('Y-m-d');
}

$last_date = new DateTime($last_time);
$this_date = new Datetime();

$this_diff = $this_date->diff($last_date);
$this_days = $this_diff->format('%a');

if ($this_days) {
cacti_log('WARNING: Configuration option "proxy_headers" will be automatically false in future releases. Please set if you require proxy IPs to be used', false, 'AUTH');
set_config_option('proxy_alert', date('Y-m-d'));
}

$proxy_headers = true;
}

/* If proxy_headers is true, allow all known headers -- NOT advised
* If proxy_headers is false, allow only REMOTE_ADDR
* IF proxy_headers is an array, filter by known headers
*/
if ($proxy_headers === true) {
$proxy_headers = $allowed_proxy_headers;
} elseif (is_array($proxy_headers)) {
$proxy_headers = array_intersect($proxy_headers, $allowed_proxy_headers);
}

if (!is_array($proxy_headers)) {
$proxy_headers = [];
}

if (!in_array('REMOTE_ADDR', $proxy_headers)) {
$proxy_headers[] = 'REMOTE_ADDR';
}

$client_addr = false;
foreach ($http_addr_headers as $header) {
foreach ($proxy_headers as $header) {
if (!empty($_SERVER[$header])) {
$header_ips = explode(',', $_SERVER[$header]);
foreach ($header_ips as $header_ip) {
Expand Down
8 changes: 4 additions & 4 deletions remote_agent.php
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ function poll_for_data() {

$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');
$poller_id = get_filter_request_var('poller_id');
$return = array();

$i = 0;
Expand Down Expand Up @@ -382,7 +382,7 @@ function poll_for_data() {
);

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);
$cactiphp = proc_open(read_config_option('path_php_binary') . ' -q ' . $config['base_path'] . '/script_server.php realtime ' . cacti_escapeshellarg($poller_id), $cactides, $pipes);
$output = fgets($pipes[1], 1024);
$using_proc_function = true;
} else {
Expand Down Expand Up @@ -443,8 +443,8 @@ function run_remote_data_query() {
function run_remote_discovery() {
global $config;

$poller_id = $config['poller_id'];
$network = get_filter_request_var('network');
$poller_id = cacti_escapeshellarg($config['poller_id']);
$network = cacti_escapeshellarg(get_filter_request_var('network'));
$php = cacti_escapeshellcmd(read_config_option('path_php_binary'));
$path = cacti_escapeshellarg(read_config_option('path_webroot') . '/poller_automation.php');

Expand Down

0 comments on commit b43f13a

Please sign in to comment.