mirror of
https://github.com/Cacti/cacti.git
synced 2026-11-02 01:57:17 +09:00
* security: add db_like_escape and bind the PTR hostname in poller_automation poller_automation.php concatenated the gethostbyaddr() result into a LIKE clause, so an attacker controlling the reverse-DNS zone injected SQL through a crafted PTR record. The new db_like_escape() helper neutralises the LIKE metacharacters, and the reverse-DNS hostname is now passed as a bound parameter (LIKE ?). The escape semantics are covered by unit tests and verified against a live MariaDB. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * security: bind and LIKE-escape the plugins.php filter (GHSA-jjfq-mj5r-2r95) The plugins.php filter reached its LIKE clauses through db_qstr(), which escapes quotes but not the LIKE metacharacters, so a filter of '%' matched every row and bypassed the state filter. The filter LIKE clauses are now bound placeholders and the value is LIKE-escaped once with db_like_escape() and reused for each placeholder; the count and list queries are prepared. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net>
34 lines
1.6 KiB
PHP
34 lines
1.6 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
/*
|
|
* plugins.php built its filter into LIKE clauses with db_qstr(), which escapes
|
|
* quotes but not the LIKE metacharacters % and _, so a filter of '%' matched
|
|
* every row and bypassed the state filter (GHSA-jjfq-mj5r-2r95). The filter is
|
|
* now a bound parameter, LIKE-escaped via db_like_escape().
|
|
*/
|
|
|
|
$src = file_get_contents(dirname(__DIR__, 2) . '/plugins.php');
|
|
|
|
test('the filter no longer reaches LIKE through db_qstr', function () use ($src) {
|
|
expect($src)->not->toContain("db_qstr('%' . grv('filter') . '%')");
|
|
});
|
|
|
|
test('the filter is bound and LIKE-escaped once, then reused for every placeholder', function () use ($src) {
|
|
expect($src)->toContain("array_fill(0, substr_count(\$sql_where, '?'), '%' . db_like_escape(grv('filter')) . '%')");
|
|
});
|
|
|
|
test('every query that embeds the filter where-clause is prepared', function () use ($src) {
|
|
// the count and list queries must pass $where_params
|
|
expect(substr_count($src, '$sql_where", $where_params);'))->toBeGreaterThanOrEqual(3);
|
|
expect($src)->toContain('db_fetch_assoc_prepared($sql, $where_params);');
|
|
// and none of them may run the where-clause through the non-prepared fetch
|
|
expect($src)->not->toContain('db_fetch_cell("SELECT COUNT(*)');
|
|
});
|