CVE-2026-44418
Description
EcclesiaCRM is CRM Software for church management. In 8.0.0 and earlier, the ValidateInput() function's default case in EcclesiaCRM's query view passes user-supplied POST parameters directly into SQL queries via str_replace without any sanitization, enabling SQL injection through query parameters that use non-standard validation types. This is caused by an incomplete fix for CVE-2026-35184.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
EcclesiaCRM 8.0.0 and earlier contains an SQL injection vulnerability in the query view due to incomplete sanitization in the ValidateInput() function's default case.
Vulnerability
Analysis
CVE-2026-44418 is an SQL injection vulnerability in EcclesiaCRM, a church management software, affecting version 8.0.0 and earlier. The root cause lies in the ValidateInput() function within src/v2/templates/query/queryview.php. When processing query parameters, the function uses a switch statement based on the parameter's validation type. For types n (numeric) and a (alpha), sanitization is applied via InputUtils::LegacyFilterInput(). However, the default case performs no sanitization, directly assigning the user-supplied POST parameter value to the variable used in the SQL query [1]. This value is then substituted into the SQL query template using str_replace, allowing an attacker to inject arbitrary SQL commands.
Exploitation
An attacker can exploit this vulnerability by sending a POST request to the query view endpoint with a parameter that has a validation type other than n or a. The parameter value is passed unsanitized into the SQL query, enabling techniques such as UNION-based data extraction, table extraction or manipulation of the database. The attack requires no authentication if the query view is accessible to unauthenticated users, though the advisory does not specify authentication requirements [1]. The vulnerability is a direct result of an incomplete fix for a previous issue, CVE-2026-35184, which was addressed by applying a blocklist-based filter (LegacyFilterInput) to the default case. However, this filter may not block all injection vectors, and the underlying use of string substitution instead of parameterized queries remains a risk [1][2].
Impact
Successful exploitation allows an attacker to execute arbitrary SQL queries, potentially leading to data exfiltration, modification, or deletion. In some database configurations, this could escalate to remote code execution through database-specific features like xp_cmdshell in SQL Server or COPY ... FROM PROGRAM in PostgreSQL [1].
Mitigation
The vendor has released a fix in commit f743b97f89da, which applies InputUtils::LegacyFilterInput() to the default case and adds additional checks to prevent empty or unset parameters from being processed [2]. Users are strongly advised to update to the patched version immediately. As a workaround, administrators should restrict access to the query view functionality to trusted users only.
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: <=8.0.0
Patches
1f743b97f89daMerge pull request #2861 from phili67/phili67-queryview-sql-injecttion-res
1 file changed · +36 −7
src/v2/templates/query/queryview.php+36 −7 modified@@ -13,6 +13,7 @@ use EcclesiaCRM\Utils\InputUtils; use EcclesiaCRM\Utils\MiscUtils; use EcclesiaCRM\dto\Cart; +use EcclesiaCRM\SessionUser; //Loops through all the parameters and ensures validation rules have been followed function ValidateInput($rsParameters, $POST) @@ -35,6 +36,8 @@ function ValidateInput($rsParameters, $POST) $aErrorText[$qrp_Alias] = _('This value is required.'); } //Assuming there was no error above... else { + $ret = ''; + //Validate differently depending on the contents of the qrp_Validation field switch ($qrp_Validation) { //Numeric validation @@ -56,7 +59,7 @@ function ValidateInput($rsParameters, $POST) } } - $vPOST[$qrp_Alias] = InputUtils::LegacyFilterInput($POST[$qrp_Alias], 'int'); + $ret = InputUtils::LegacyFilterInput($POST[$qrp_Alias], 'int'); break; //Alpha validation @@ -72,13 +75,17 @@ function ValidateInput($rsParameters, $POST) $aErrorText[$qrp_Alias] = _('This value cannot be less than ') . $qrp_AlphaMinLength . _(' characters long'); } - $vPOST[$qrp_Alias] = InputUtils::LegacyFilterInput($POST[$qrp_Alias]); + $ret = InputUtils::LegacyFilterInput($POST[$qrp_Alias]); break; default: - $vPOST[$qrp_Alias] = $POST[$qrp_Alias]; + $ret = InputUtils::LegacyFilterInput($POST[$qrp_Alias]); break; } + + if (!empty($ret) || $ret === '0') { + $vPOST[$qrp_Alias] = $ret; + } } } @@ -95,10 +102,16 @@ function ProcessSQL($vPOST, $qry_SQL, $rsParameters) while ($aRow = mysqli_fetch_array($rsParameters)) { extract($aRow); + if (!isset($vPOST[$qrp_Alias])) { + return ""; + } + //Debugging code + if (SessionUser::getUser()->isAdmin()) { ?> <?= "--" . $qry_SQL ?><br>-- ~<?= $qrp_Alias ?>~<br>--<?= $vPOST[$qrp_Alias] ?><p> <?php + } //Replace the placeholder with the parameter value $qry_SQL = str_replace('~' . $qrp_Alias . '~', $vPOST[$qrp_Alias], $qry_SQL); } @@ -238,15 +251,23 @@ class="btn btn-danger btn-sm"> <i class="fas fa-times"></i> <?= _('Remove From C </div> </div> + </div> + <?php if (SessionUser::getUser()->isAdmin()) { ?> <div class="card card-info"> <div class="card-header border-1"> <div class="card-title">Query</div> </div> <div class="card-body"> - <code><?= str_replace(chr(13), '<br>', htmlspecialchars($qry_SQL)); ?></code> + <code> + <?php + echo str_replace(chr(13), '<br>', htmlspecialchars($qry_SQL)); + ?> + </code> </div> </div> + <?php } + ?> <script nonce="<?= $CSPNonce ?>"> var aAddToCartIDs = <?= json_encode($aAddToCartIDs) ?>; @@ -450,8 +471,9 @@ function DisplayParameterForm($rsParameters, $iQueryID, $sRootPath) } ?> <div class="form-group text-right"> - <input class="btn btn-primary" type="Submit" value="<?= _("Execute Query") ?>" - name="Submit"> + <button class="btn btn-success btn-lg shadow-sm font-weight-bold py-2 px-4" type="submit" name="Submit"> + <i class="fas fa-play mr-2"></i> <?= _("Execute Query") ?> + </button> </div> </form> @@ -488,7 +510,14 @@ function DisplayParameterForm($rsParameters, $iQueryID, $sRootPath) //No errors; process the SQL, run the query, and display the results DisplayQueryInfo($qry_Name, $qry_Description); $qry_SQL = ProcessSQL($vPOST, $qry_SQL, $rsParameters); - DoQuery($cnInfoCentral, $aRowClass, $rsQueryResults, $qry_SQL, $iQueryID, $qry_Name, $qry_Count, $CSPNonce, $sRootPath); + if (empty($qry_SQL)) { + echo '<div class="alert alert-danger">' . _('An error occurred while processing the SQL for this query. Please check your parameter values and try again.') . '</div>'; + DisplayParameterForm($rsParameters, $iQueryID, $sRootPath); + require $sRootDocument . '/Include/Footer.php'; + exit; + } else { + DoQuery($cnInfoCentral, $aRowClass, $rsQueryResults, $qry_SQL, $iQueryID, $qry_Name, $qry_Count, $CSPNonce, $sRootPath); + } } else { //Yes, there were errors; re-display the parameter form (the DisplayParameterForm function will //pick up and display any error messages)
Vulnerability mechanics
Root cause
"The default case in ValidateInput() passes user-supplied POST parameters directly into SQL queries via str_replace without sanitization, enabling SQL injection through query parameters that use non-standard validation types."
Attack vector
An attacker submits a crafted POST parameter to the query execution endpoint where the corresponding query parameter's `qrp_Validation` field does not match any of the known validation types (numeric, alpha, etc.). In the default case of the switch statement, the raw POST value was previously assigned directly to `$vPOST[$qrp_Alias]` without any sanitization [CWE-89]. This unsanitized value is then substituted into the SQL query via `str_replace` in `ProcessSQL()`, allowing arbitrary SQL injection. The attacker only needs access to a query that has a parameter with a non-standard validation type, or the ability to create/modify such a query parameter.
Affected code
The vulnerability is in `src/v2/templates/query/queryview.php` within the `ValidateInput()` function. The default case of the switch statement (line ~75 in the original) assigned the raw POST value directly to `$vPOST[$qrp_Alias]` without sanitization. The `ProcessSQL()` function then substituted this unsanitized value into the SQL query via `str_replace`.
What the fix does
The patch applies `InputUtils::LegacyFilterInput()` to the POST value in the default case, ensuring it receives the same sanitization as the alpha validation path [patch_id=424516]. It also introduces a guard in `ProcessSQL()` that returns an empty string if `$vPOST[$qrp_Alias]` is not set, preventing uninitialized variable substitution. Additionally, the patch restricts debug output (which previously leaked the raw SQL with substituted values) to admin users only, and adds an error-handling block that stops execution when `ProcessSQL` returns an empty string, preventing malformed queries from being executed.
Preconditions
- authAttacker must be an authenticated user with access to the query view functionality.
- inputThe query parameter must have a qrp_Validation value that does not match 'int' or alpha validation types, causing the default case to execute.
Generated on May 19, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.