mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-26 04:58:21 +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>
61 lines
2.6 KiB
PHP
61 lines
2.6 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
| |
|
|
| This program is free software; you can redistribute it and/or |
|
|
| modify it under the terms of the GNU General Public License |
|
|
| as published by the Free Software Foundation; either version 2 |
|
|
| of the License, or (at your option) any later version. |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
/*
|
|
* db_like_escape() neutralises the LIKE metacharacters so a value matches
|
|
* literally under a bound LIKE ? clause. The MySQL semantics (that '\%' and '\_'
|
|
* become the literal characters) are verified separately against a live MariaDB;
|
|
* these tests pin the escaping the helper produces.
|
|
*/
|
|
|
|
require_once dirname(__DIR__) . '/Helpers/UnitStubs.php';
|
|
require_once dirname(__DIR__, 2) . '/lib/database.php';
|
|
|
|
test('escapes percent, underscore and backslash for LIKE', function () {
|
|
expect(db_like_escape('100%'))->toBe('100\\%');
|
|
expect(db_like_escape('a_b'))->toBe('a\\_b');
|
|
expect(db_like_escape('c\\d'))->toBe('c\\\\d');
|
|
});
|
|
|
|
test('escapes the backslash before the wildcards so nothing is left unescaped', function () {
|
|
// a raw backslash must not become a LIKE escape for a following char
|
|
expect(db_like_escape('\\%'))->toBe('\\\\\\%');
|
|
});
|
|
|
|
test('leaves ordinary text untouched', function () {
|
|
expect(db_like_escape('router-01.example.net'))->toBe('router-01.example.net');
|
|
expect(db_like_escape(''))->toBe('');
|
|
});
|
|
|
|
test('neutralises a wildcard-injection payload', function () {
|
|
// the reported filter/PTR payloads relied on an unescaped % matching every row
|
|
expect(db_like_escape('%'))->toBe('\\%');
|
|
expect(db_like_escape('foo%" OR "1"="1'))->toContain('\\%');
|
|
expect(db_like_escape('_'))->toBe('\\_');
|
|
});
|
|
|
|
test('returns a string for non-string input', function () {
|
|
expect(db_like_escape(12345))->toBe('12345');
|
|
expect(db_like_escape(null))->toBe('');
|
|
});
|
|
|
|
test('poller_automation binds the PTR hostname with db_like_escape', function () {
|
|
$src = file_get_contents(dirname(__DIR__, 2) . '/poller_automation.php');
|
|
|
|
// the reverse-DNS hostname must reach LIKE via a bound parameter, not concat
|
|
expect($src)->toContain('hostname = ? OR hostname LIKE ?');
|
|
expect($src)->toContain('db_like_escape($hostname) . \'%\'');
|
|
expect($src)->not->toContain('hostname LIKE "\' . $hostname . \'%"');
|
|
});
|