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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions