A newly disclosed vulnerability in the Grav CMS Database plugin allows attackers to execute arbitrary SQL commands against backend databases, putting site credentials, user data, and configuration details at risk.
Tracked as CVE-2026-58492 and rated Moderate severity, the flaw affects getgrav/grav-plugin-database version 1.1.2 and was patched in version 1.2.0.
Security researcher rhukster published the advisory (GHSA-8jxg-4pw9-xcwf) three weeks ago, crediting nicl4ssic for the discovery. The vulnerability falls under CWE-89: Improper Neutralization of Special Elements used in an SQL Command, a classic yet still dangerous class of bugs that continues to plague web applications despite decades of awareness.
Grav CMS SQL Injection Flaw
The root cause lies in the plugin’s tableExists() method, located in user/plugins/database/classes/PDO.php (lines 42–55). The function checks whether a database table exists by running a raw query, but it builds that query using unsanitized string interpolation:
public function tableExists($table)
{
try {
$result = $this->query("SELECT 1 FROM $table LIMIT 1");
} catch (\PDOException $e) {
return false;
}
return $result !== false;
}
The $table parameter is dropped directly into the SQL string with no escaping, quoting, or whitelisting. The surrounding try/catch block only suppresses PDOException errors for missing tables; it does nothing to stop injected SQL from executing first. By the time an exception could fire, the malicious query has already run.
Rhukster demonstrated the flaw using a SQLite backend. Passing a crafted string like:
$malicious = "test; CREATE TABLE injected (id INTEGER PRIMARY KEY); --";
$db->sqlite('default')->tableExists($malicious);
successfully created a new table in the database, confirming code execution beyond the intended query. A follow-up test using UNION SELECT sql FROM sqlite_master extracted the entire database schema, showing that even without multi-statement support, attackers can exfiltrate data through subqueries and UNION-based injection.
On MySQL configurations with PDO::MYSQL_ATTR_MULTI_STATEMENTS enabled, or on PostgreSQL, the impact could be more severe.
The vulnerability itself doesn’t grant remote access by default; exploitation depends on whether a consuming plugin or custom code passes attacker-influenced input (URL parameters, form fields, uploaded file metadata) into tableExists(). Where that condition is met, the consequences include:
- Information disclosure of credentials, PII, or configuration data
- Unauthorized modification or deletion of database records
- Privilege escalation via tampered user roles or access tokens
- Potential filesystem writes on database engines supporting
INTO OUTFILEorCOPY ... TO
Mitigation
Grav has resolved the issue in plugin version 1.2.0. Site administrators and developers should:
- Upgrade
grav-plugin-databaseto 1.2.0 or later immediately - Audit custom plugins or code for any calls to
tableExists()with user-influenced input - Apply parameterized queries or strict whitelisting for any dynamic table/column names going forward
- Review database logs for anomalous queries if the plugin has been in production
This case underscores a persistent lesson in secure coding: even utility functions meant for simple existence checks can become injection vectors when developers assume table names are inherently “safe” inputs.
Given SQL injection’s continued prevalence on the OWASP Top 10, plugin maintainers across CMS ecosystems should treat this as a reminder to audit similar interpolation patterns in their own codebases.