Skip to content

hardening: escape device hostname output in syslog view; parameterize alert API functions #252

@somethingwithproof

Description

@somethingwithproof

Reviewed syslog.php and syslog_alerts.php at commit c64bcca. Two hardening items worth addressing.

Unescaped device hostname in HTML output (syslog.php)

Three locations print the device hostname directly into HTML without html_escape():

// line 367 -- <td> cell
print '<td>' . (get_request_var('host') != '-2' ? $r['host']:'-') . '</td>';

// line 524 -- <option> element
print '>' . $host['host'] . '</option>';

// line 1352 -- <option> element (after syslog_strip_domain)
print $host['host'] . '</option>';

If a device hostname in the Cacti database contains HTML special characters, they render as markup for all users viewing the syslog page. The fix is straightforward -- wrap each with html_escape():

print '<td>' . html_escape($r['host']) . '</td>';
print '>' . html_escape($host['host']) . '</option>';

All exploitable paths require an authenticated Cacti session and a device hostname containing markup in the database.


Unparameterized alert API functions (syslog_alerts.php)

api_syslog_alert_remove(), api_syslog_alert_disable(), and api_syslog_alert_enable() (lines 327-339) concatenate $id directly into SQL without internal validation:

function api_syslog_alert_remove($id) {
    syslog_db_execute("DELETE FROM `...`.`syslog_alert` WHERE id='" . $id . "'");
}

Current call sites pass integers from sanitize_unserialize_selected_items(), so there is no active injection path. Adding intval($id) at the top of each function closes the risk for any future caller without changing behavior:

function api_syslog_alert_remove($id) {
    $id = intval($id);
    syslog_db_execute("DELETE FROM `...`.`syslog_alert` WHERE id='" . $id . "'");
}

Or switch to syslog_db_execute_prepared() if available.


Investigated and ruled out

  • $hfilter in IN() clause (syslog.php): validated by FILTER_VALIDATE_IS_NUMERIC_LIST. Safe.
  • rfilter SQL breakout: validate_is_regex() uses ' as the preg_match delimiter, blocking single-quote input. Not injectable.
  • Retention DELETE and confirmation dialog fetch: not injectable in practice (date format / digits-only regex), but inconsistent with parameterized calls elsewhere.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions