Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## ChangeLog

--- develop ---

* security: Escape WMI account, query, and tool output on render (html_escape/__esc) to close stored and reflected XSS
* security: Bind the remaining interpolated SQL as prepared statements in functions.php, poller_wmi.php, and script/wmi-script.php
* security: Remove wmi_accounts.php and wmi_tools.php from the Template Editor auth augment so credential management and the live query tool stay behind the WMI Management realm
* security: Escape the wmic hostname and namespace before exec so a device-supplied address cannot inject a shell command (issue#5)
* security: Quote the wmic delimiter so exec() no longer splits the command into a shell pipeline (issue#5)
* security: Restrict decode() unserialize with allowed_classes to block PHP object injection (issue#5)
8 changes: 4 additions & 4 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function plugin_wmi_query_exists($query) {

foreach($tokens as $token) {
if ($next_ic) {
$exists = db_fetch_cell("SELECT COUNT(*) FROM wmi_wql_queries WHERE query RLIKE '^FROM\s$token$+'");
$exists = db_fetch_cell_prepared('SELECT COUNT(*) FROM wmi_wql_queries WHERE query RLIKE ?', array('^FROM\s' . $token . '$+'));
}

if (strtolower($token) == 'from') {
Expand All @@ -71,7 +71,7 @@ function plugin_wmi_create_dataquery_xml($id) {
global $config;

include_once($config['base_path'] . '/lib/export.php');
$wmic = db_fetch_row("SELECT * FROM wmi_wql_queries WHERE id = $id");
$wmic = db_fetch_row_prepared('SELECT * FROM wmi_wql_queries WHERE id = ?', array($id));
$data = '';
if (isset($wmic['id'])) {
$data = "<cacti>\n";
Expand Down Expand Up @@ -174,7 +174,7 @@ function plugin_wmi_create_dataquery_xml($id) {
}
$data .= "\t\t</items>\n";

$data_input_data = db_fetch_assoc("SELECT * FROM data_input_fields WHERE data_input_fields.data_input_id=$input AND input_output = 'in' ORDER BY id DESC");
$data_input_data = db_fetch_assoc_prepared("SELECT * FROM data_input_fields WHERE data_input_fields.data_input_id = ? AND input_output = 'in' ORDER BY id DESC", array($input));
$data .= "\t\t<data>\n";
$i = 0;
if (cacti_sizeof($data_input_data) > 0) {
Expand Down Expand Up @@ -278,7 +278,7 @@ function plugin_wmi_create_dataquery_xml($id) {
}

function plugin_wmi_create_resource_xml($id) {
$wmic = db_fetch_row("SELECT * FROM wmi_wql_queries WHERE id = $id");
$wmic = db_fetch_row_prepared('SELECT * FROM wmi_wql_queries WHERE id = ?', array($id));
$data = '';
if (isset($wmic['id'])) {
$data = "<WMIQuery>\n";
Expand Down
10 changes: 5 additions & 5 deletions poller_wmi.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ function process_all_devices() {
/* put a placeholder in place to prevent overloads on slow systems */
$key = rand();

db_execute("INSERT INTO wmi_processes (pid, taskid, started) VALUES ($key, $seed, NOW())");
db_execute_prepared('INSERT INTO wmi_processes (pid, taskid, started) VALUES (?, ?, NOW())', array($key, $seed));

print "NOTE: Launching WMI Collector For: '" . $device['description'] . '[' . $device['hostname'] . "]'" . PHP_EOL;

Expand All @@ -210,7 +210,7 @@ function process_all_devices() {

/* wait for all processes to end or max run time */
while (true) {
$processes_left = db_fetch_cell("SELECT COUNT(*) FROM wmi_processes WHERE taskid = $seed");
$processes_left = db_fetch_cell_prepared('SELECT COUNT(*) FROM wmi_processes WHERE taskid = ?', array($seed));
$pl = db_fetch_cell('SELECT COUNT(*) FROM wmi_processes');

if ($processes_left == 0) {
Expand Down Expand Up @@ -296,8 +296,8 @@ function process_device($host_id) {
array($host_id));

/* remove the key process and insert the set a process lock */
db_execute('REPLACE INTO wmi_processes (pid, taskid) VALUES (' . getmypid() . ", $seed)");
db_execute("DELETE FROM wmi_processes WHERE pid = $key AND taskid = $seed");
db_execute_prepared('REPLACE INTO wmi_processes (pid, taskid) VALUES (?, ?)', array(getmypid(), $seed));
db_execute_prepared('DELETE FROM wmi_processes WHERE pid = ? AND taskid = ?', array($key, $seed));

$qstart = date('Y-m-d H:i:s');

Expand Down Expand Up @@ -355,7 +355,7 @@ function process_device($host_id) {
}

/* remove the process lock */
db_execute('DELETE FROM wmi_processes WHERE pid=' . getmypid());
db_execute_prepared('DELETE FROM wmi_processes WHERE pid = ?', array(getmypid()));

if ($wmi_errors > 0) {
cacti_log("WARNING: WMI Device[$host_id] experienced $wmi_errors WMI Errors while performing data collection. Increase logging to HIGH for this device to see the errors.", false, 'WMI');
Expand Down
2 changes: 1 addition & 1 deletion script/wmi-script.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function wmi_script($hostname, $host_id, $wmiquery, $cmd = '', $arg1 = '', $arg2
$wmi->binary = $config['base_path'] . '/plugins/wmi/wmic';

/* Fetch the info for this WMI query from the database, exit if not found */
$wmiinfo = db_fetch_row("SELECT * FROM plugin_wmi_queries WHERE queryname = '$wmiquery'", FALSE);
$wmiinfo = db_fetch_row_prepared('SELECT * FROM plugin_wmi_queries WHERE queryname = ?', array($wmiquery));
if (!isset($wmiinfo['queryclass'])) {
return '';
}
Expand Down
6 changes: 5 additions & 1 deletion setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,11 @@ function wmi_config_arrays() {
$menu[__('Data Collection')]['plugins/wmi/wmi_queries.php'] = __('WMI Queries', 'wmi');

if (function_exists('auth_augment_roles')) {
auth_augment_roles(__('Template Editor'), array('wmi_accounts.php', 'wmi_queries.php', 'wmi_tools.php'));
// Template Editors define WMI queries as part of template work. Credential
// management (wmi_accounts.php) and the live query tool (wmi_tools.php,
// which reaches Linux_WMI::exec) stay behind the dedicated WMI Management
// realm rather than being handed to every Template Editor.
auth_augment_roles(__('Template Editor'), array('wmi_queries.php'));
}
}

Expand Down
4 changes: 2 additions & 2 deletions wmi_accounts.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ function edit_accounts() {
$account = db_fetch_row_prepared('SELECT * FROM wmi_user_accounts WHERE id = ?', array(get_request_var('id')));

$account['password'] = '';
$header_label = __('Account [edit: %s]', $account['name'], 'wmi');
$header_label = __esc('Account [edit: %s]', $account['name'], 'wmi');
}else{
$header_label = __('Account [new]', 'wmi');
}
Expand Down Expand Up @@ -416,7 +416,7 @@ function show_accounts() {

form_alternate_row('line' . $row['id'], false);
form_selectable_cell(filter_value($row['name'], get_request_var('filter'), 'wmi_accounts.php?&action=edit&id=' . $row['id']), $row['id']);
form_selectable_cell($row['username'], $row['id']);
form_selectable_cell(html_escape($row['username']), $row['id']);
form_selectable_cell(number_format_i18n($count), $row['id'], '', 'right');
form_checkbox_cell($row['name'], $row['id']);
form_end_row();
Expand Down
10 changes: 5 additions & 5 deletions wmi_queries.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ function edit_queries() {
WHERE id= ?',
array(get_filter_request_var('id')));

$header_label = __('Query [edit: %s]', $query['name'], 'wmi');
$header_label = __esc('Query [edit: %s]', $query['name'], 'wmi');
}else{
$header_label = __('Query [new]', 'wmi');
}
Expand Down Expand Up @@ -452,12 +452,12 @@ function show_queries() {
if (!empty($queries)) {
foreach ($queries as $row) {
form_alternate_row('line' . $row['id'], true);
form_selectable_cell('<a class="linkEditMain" href="' . htmlspecialchars('wmi_queries.php?&action=edit&id=' . $row['id']) . '">' . $row['name'] . '</a>', $row['id']);
form_selectable_cell('<a class="linkEditMain" href="' . htmlspecialchars('wmi_queries.php?&action=edit&id=' . $row['id']) . '">' . html_escape($row['name']) . '</a>', $row['id']);
form_selectable_cell($row['id'], $row['id'], '', 'right');
form_selectable_cell($row['frequency'], $row['id'], '', 'right');
form_selectable_cell($row['namespace'], $row['id']);
form_selectable_cell($row['query'], $row['id']);
form_selectable_cell($row['primary_key'], $row['id']);
form_selectable_ecell($row['namespace'], $row['id']);
form_selectable_ecell($row['query'], $row['id']);
form_selectable_ecell($row['primary_key'], $row['id']);
form_checkbox_cell($row['name'], $row['id']);
form_end_row();
}
Expand Down
16 changes: 8 additions & 8 deletions wmi_tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ function walk_host() {
$class = $wmi->fetch_class();
$data = $wmi->fetch_data();

print "<h4>" . __('WMI Query Results for Device: %s, Class: %s, Columns: %s, Rows: %s', $host, $class, sizeof($indexes), sizeof($data), 'wmi') . "</h4>";
print "<h4>" . __esc('WMI Query Results for Device: %s, Class: %s, Columns: %s, Rows: %s', $host, $class, sizeof($indexes), sizeof($data), 'wmi') . "</h4>";

print "<p>" . __('Showing columns and first one or two rows of data.', 'wmi') . "</p>";

Expand All @@ -563,10 +563,10 @@ function walk_host() {
foreach($data[0] as $index => $r) {
form_alternate_row('line' . $index, true);

print "<td style='font-weight:bold;'>" . $indexes[$index] . "</td><td>" . $r . "</td>";
print "<td style='font-weight:bold;'>" . html_escape($indexes[$index]) . "</td><td>" . html_escape($r) . "</td>";

if (isset($data[1][$index])) {
print "<td style='font-weight:bold;'>" . $indexes[$index] . "</td><td>" . $data[1][$index] . "</td>";
print "<td style='font-weight:bold;'>" . html_escape($indexes[$index]) . "</td><td>" . html_escape($data[1][$index]) . "</td>";
}

form_end_row();
Expand All @@ -578,14 +578,14 @@ function walk_host() {
if (cacti_sizeof($indexes)) {
print "<tr>";
foreach($indexes as $col) {
print "<th>" . $col . "</th>";
print "<th>" . html_escape($col) . "</th>";
}
print "</tr>";
}

print "<tr>";
foreach($row as $data) {
print "<td>" . $data . "</td>";
print "<td>" . html_escape($data) . "</td>";
}
print "</tr>";
}
Expand Down Expand Up @@ -614,7 +614,7 @@ function walk_host() {

print "<table style='width:100%'><tr><td>";

print "<h4>" . __('WMI Query Results for Device: %s, Class: %s, Columns: %s, Rows: %s', $host, $namespace, sizeof($indexes), sizeof($data), 'wmi') . "</h4>";
print "<h4>" . __esc('WMI Query Results for Device: %s, Class: %s, Columns: %s, Rows: %s', $host, $namespace, sizeof($indexes), sizeof($data), 'wmi') . "</h4>";

print "<p>" . __('Showing columns and first one or two rows of data.', 'wmi') . "</p>";

Expand All @@ -625,10 +625,10 @@ function walk_host() {
foreach($odata as $index => $r) {
form_alternate_row('line' . $index, true);

print "<td style='font-weight:bold;'>" . $indexes[$index] . "</td><td>" . $r . "</td>";
print "<td style='font-weight:bold;'>" . html_escape($indexes[$index]) . "</td><td>" . html_escape($r) . "</td>";

if (cacti_sizeof($odata1)) {
print "<td style='font-weight:bold;'>" . $indexes[$index] . "</td><td>" . $odata1[$index] . "</td>";
print "<td style='font-weight:bold;'>" . html_escape($indexes[$index]) . "</td><td>" . html_escape($odata1[$index]) . "</td>";
}

form_end_row();
Expand Down