Introduction
Apache Fineract sits underneath loan books, savings accounts, and general ledgers for microfinance and core banking deployments. That means an SQL injection in its search layer reaches directly into the data store behind financial records. In CVE-2026-64618, which NVD scores 9.8 Critical, the comparison operator in an advanced search request flows straight into the SQL text that Fineract builds for loan outstanding amount filters.
Apache Fineract is an open-source core banking platform governed by the Apache Software Foundation. It provides client data management, loan and savings portfolio management, accounting, and reporting, with cloud and on-premises deployment options. It graduated to an ASF Top-Level Project on April 24, 2017.
The ASF announcement cited Musoni using Fineract as a core component of a cloud banking platform serving more than 90 microfinance institutions across Africa. Fineract is also the backend of the Mifos X distribution, which places it at the center of a wider financial inclusion ecosystem.
Technical information
Summary
| Attribute | Value |
|---|---|
| Product | Apache Fineract |
| Weakness | CWE-89, SQL injection |
| Vulnerable input | outStandingAmountPercentageCondition |
| Endpoint | /fineract-provider/api/v1/search/advance |
| NVD score | CVSS 3.1 9.8 Critical |
| Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Apache severity label | important |
| Affected | 1.15.0 and earlier |
| Fixed | 1.16.1 |
Root cause: an operator cannot be a bind parameter
The advanced search endpoint lets a caller filter loans by outstanding percentage or outstanding amount. To do that, it combines a comparison operator such as <= with a numeric value.
The value is handled safely as a named bind parameter, :outStandingAmountPercentage. The operator cannot be bound, because SQL bind parameters only carry values, not syntax. Fineract therefore concatenates the operator string into the query text, producing fragments like this:
a.percentOut <= :outStandingAmountPercentage
Before the patch, convertToSearchConditions() in SearchReadServiceImpl.java only screened these operator strings through the general-purpose SqlValidator:
if (request.getOutStandingAmountPercentageCondition() != null) { sqlValidator.validate(request.getOutStandingAmountPercentageCondition()); } if (request.getOutstandingAmountCondition() != null) { sqlValidator.validate(request.getOutstandingAmountCondition()); }
After that check, the code passed the raw request value to the query builder. A pattern-based validator tries to spot bad input. When the input is concatenated directly into SQL, any gap in that detection becomes injection. The validator was the only barrier between attacker-controlled text and the SQL statement.
Two neighboring inputs reached the same query through the same weak path:
outstandingAmountCondition, the sibling of the named parameter, used the same pattern.loanDateOptionwas also only run throughSqlValidator.
Attack flow
Based on the advisory and the published fix, exploitation follows this shape:
- The attacker locates a Fineract instance at 1.15.0 or earlier that exposes the advanced search API at
/api/v1/search/advance. - The attacker submits a search request that enables outstanding amount percentage filtering. The
outStandingAmountPercentageConditionvalue starts with a valid operator and continues with additional SQL syntax. Fineract's own regression test uses the shape<= 0 union selectas a rejected example. - If
SqlValidatordoes not flag the string, the raw value is placed intoAdHocQuerySearchConditions. It is then concatenated next toa.percentOutin the query text. - The database executes a statement whose structure is now partly defined by the attacker. The same approach applies to
outstandingAmountCondition, which the tests probe with>= 0 or 1 = 1.
The CVSS vector models high confidentiality, integrity, and availability impact. That is a scoring model. It does not demonstrate data extraction or transaction manipulation in a specific deployment.
The advisory does not publish a complete malicious HTTP request. The reviewed patch material also does not establish the HTTP verb or the concrete authorization checks in front of the endpoint. Treat NVD's PR:N rating as NVD's assessment rather than as source-level proof that every deployment allows unauthenticated access to this path.
Threat context
Public reporting describes a serious weakness with no observed campaign:
- SecurityOnline reports that maintainers have not seen active exploitation and that no public proof of concept has been released.
- TheHackerWire reports no confirmed public exploit code and no known ransomware linkage.
- NotCVE and TheHackerWire report that the CVE is not listed in CISA KEV.
- NotCVE puts EPSS below 1%, while TheHackerWire lists scoring as in progress.
Apache credits thiscodecc of MoyunSec Vlab and Bing as the finders, and Adam Saghy as the remediation developer.
Disclosure timeline
| Event | Date |
|---|---|
| Report received by Apache | April 10, 2026 |
| Fix merged (PR 6153) | July 23, 2026 |
| Fineract 1.16.1 release announcement | October 29, 2026 |
| NVD publication | October 30, 2026 |
| NVD last modification | November 2, 2026 |
Patch information
Apache fixed CVE-2026-64618 in Apache Fineract 1.16.1.
- Change: PR 6153, titled FINERACT-2684: Enhance Search API, written by Ádám Sághy.
- Merge: Merged into
developon July 23, 2026, as merge commit355fd6fcb67f7255726ddbd94f10c66ed190b7db. - Advisory: The Apache advisory on oss security lists this PR as the reference fix.
The PR is small. It has one commit, with 149 additions and 19 deletions across three files:
SearchReadServiceImpl.java(search service): the actual fix.SearchConfiguration.java(search starter): wiring cleanup.SearchReadServiceImplTest.java: new regression tests.
From detection to an allowlist
The patch removes SqlValidator from the search service entirely and adds a strict allowlist for the operator:
private static final String VALID_SEARCH_CONDITIONS = "=, >, >=, <, <=, between"; ... private static String validateSearchCondition(final String condition, final String parameterName, final boolean required) { if (StringUtils.isBlank(condition)) { if (required) { throw invalidSearchParameter(parameterName, condition, VALID_SEARCH_CONDITIONS); } return condition; } final String trimmedCondition = condition.trim(); if (CONDITION_BETWEEN.equalsIgnoreCase(trimmedCondition)) { return CONDITION_BETWEEN; } ...
The method finishes with a switch expression. It returns trimmedCondition only when the value is exactly =, >, >=, <, or <=, and it throws invalidSearchParameter for anything else.
The method has three properties:
- After trimming, the input must equal one of six tokens. Any extra character fails the
switch, including a trailing0 union select. Before the patch, the only question was whether the validator noticed something bad. - The method returns either a trimmed exact match or the constant
between, never the original string.convertToSearchConditions()now buildsAdHocQuerySearchConditionsfrom these validated values instead of fromrequest.get...(). The SQL builder therefore only ever sees an allowed operator. - The check fails closed. If
includeOutStandingAmountPercentageorincludeOutstandingAmountistruebut the matching condition is blank, the request is rejected. Rejected input raisesPlatformApiDataValidationExceptionwith the error keyvalidation.msg.search.parameter.is.not.one.of.expected.enumerations. The API returns a validation error and no query runs.
Neighboring inputs
The same PR hardens the two related inputs:
outstandingAmountConditiongoes through the same allowlist.loanDateOptionmust now be blank or one of the values fromSearchConstants.SearchLoanDate.getAllValues():
private static void validateLoanDateOption(final String loanDateOption) { if (StringUtils.isBlank(loanDateOption) || SearchConstants.SearchLoanDate.getAllValues().contains(loanDateOption)) { return; }
Because the service no longer needs SqlValidator, the SearchConfiguration bean factory drops it from the SearchReadServiceImpl constructor. This is cleanup only, with no behavior change.
Regression tests
The new SearchReadServiceImplTest covers both sides of the fix:
- A valid
<=condition produces SQL containinga.percentOut <= :outStandingAmountPercentage. - An injected
outStandingAmountPercentageConditionof<= 0 union selectthrowsPlatformApiDataValidationException. - An injected
outstandingAmountConditionof>= 0 or 1 = 1is rejected the same way.
In both rejection tests, Mockito verifies that NamedParameterJdbcTemplate.query(...) is never() called. Validation stops bad input before any SQL is built or run.
Backports and hardening
The fix ships in the 1.16.1 release line. Since January 2025, Fineract's stated policy has been to decide backports case by case, and older releases become end of life by default when a new one ships. The advisories name no backported patch for 1.15.x or earlier.
The advisory provides no CVE-specific workaround. Fineract's SECURITY.md lists general production controls that complement the upgrade but do not fix the input handling:
- Keep Fineract behind a reverse proxy, WAF, or API gateway rather than exposing it directly to the internet.
- Drop migration-only database privileges after startup.
- Use OAuth2 through a trusted identity provider instead of Basic Auth.
- Enable two-factor authentication for administrators.
Affected systems and versions
- Affected: Apache Fineract 1.15.0 and earlier, per the Apache advisory and the Fineract security page.
- Fixed: Apache Fineract 1.16.1. The Fineract security page lists CVE-2026-64618 under "Fixed in 1.16.1".
- Vulnerable component: The
outStandingAmountPercentageConditionparameter of/api/v1/search/advance. The siblingoutstandingAmountConditionandloanDateOptioninputs were hardened in the same change. - Version nuance: The NVD machine-readable configuration marks versions before 1.16.0 as vulnerable, while Apache's prose names 1.15.0 and earlier. Neither source confirms 1.16.0 as vulnerable, but Apache's explicit upgrade target is 1.16.1.
- Backports: None named for 1.15.x or earlier.
Vendor security history
Fineract has published several SQL injection advisories affecting different API paths:
| CVE | Mechanism | Affected releases and fix |
|---|---|---|
| CVE-2024-23538 | Blind SQL injection through sqlSearch under certain configurations | 1.8.4 and earlier. Fix recorded December 6, 2023. |
| CVE-2024-23539 | SQL injection through sqlSearch on specific endpoints under certain configurations | 1.8.4 and earlier. Fix recorded December 6, 2023. |
| CVE-2026-64618 | Advanced Search API injection through outStandingAmountPercentageCondition | 1.15.0 and earlier. Fix recorded July 23, 2026, listed under 1.16.1. |
| CVE-2026-65874 | Time-based blind injection through ORDER BY inputs in Center and datatable endpoints. Regex validation can be bypassed. | 1.15.0 and earlier. Fix recorded August 18, 2026. |
CVE-2026-65874 involved regex validation that could be bypassed. The CVE-2026-64618 fix abandons detection in favor of an exact-match allowlist. These are separate defects in separate inputs, so do not assume a fix for one covers the other.
Apache publishes resolved issues on its security page and accepts reports at [email protected].
References
- NVD entry for CVE-2026-64618
- CVE Record for CVE-2026-64618
- NVD API record
- Apache advisory on the ASF mailing list
- Openwall oss security announcement (http)
- Openwall oss security announcement (https)
- Apache Fineract Security Reports
- GitHub PR 6153, Enhance Search API
- PR 6153 files changed
- GitHub API: PR 6153
- GitHub API: merge commit 355fd6f
- Fineract 1.16.1 release announcement
- Fineract releases on GitHub
- Fineract 1.16.1 documentation
- Fineract SECURITY.md
- GitHub Advisory Database entry
- TheHackerWire analysis
- SecurityOnline coverage of Apache patches
- NotCVE entry
- CISA Known Exploited Vulnerabilities Catalog
- Fineract security hardening checklist (Finecko)
- Apache Fineract project site
- ASF announcement of Fineract as a Top Level Project
- Mifos X
- Apache Fineract Security Report wiki
- Hosting Experiences from Partners wiki



