CVE-2026-22243

Typecast Catastrophe: The EGroupware JSON-to-SQL Pipeline

Alon Barad
Alon Barad
Software Engineer

Jan 28, 2026·6 min read·6 visits

Executive Summary (TL;DR)

EGroupware trusted JSON integers to be safe. They weren't. By sending native integers in a JSON payload, attackers bypass input sanitization that expects strings, injecting unquoted values directly into SQL queries. This allows for database type confusion and unauthorized data access.

A high-severity SQL injection vulnerability in EGroupware's Nextmatch widget allows authenticated attackers to manipulate database queries via JSON type juggling. By leveraging PHP's strict integer handling against a database's implicit casting, attackers can bypass quoting mechanisms and potentially exfiltrate sensitive data or modify records.

The Hook: When Types Don't Match

In the world of web security, we often obsess over strings. We strip tags, we escape quotes, and we panic over apostrophes. But what happens when the input isn't a string? EGroupware, a robust collaboration platform used by enterprises, fell into a classic trap: assuming that if something looks like a number and smells like a number, it can't possibly be a weapon.

The vulnerability resides in the Nextmatch widget, the core component responsible for rendering lists, calendars, and address books. It's the gatekeeper that decides what you see. To handle complex filtering, Nextmatch accepts JSON payloads. And this is where the plot thickens. JSON supports native types—booleans, nulls, and most importantly, integers.

PHP, being the loosely-typed chaotic neutral language we all love (and fear), interacts with these JSON types in interesting ways. When a developer writes code expecting a standard HTTP POST request (where everything is a string), they might not anticipate the behavior of json_decode. This cognitive gap turned a routine filter feature into a high-severity hole, earning it a CVSS score of 8.7.

The Flaw: The Integer Bypass

The root cause of CVE-2026-22243 is a subtle logic error in how filters are sanitized. The application logic followed a seemingly sound principle: strict validation. Before putting a user-supplied value into a SQL WHERE clause, the code checked its type.

Here is the logic in pseudocode: "If the value is an integer, it's safe. Append it. If it's anything else (like a string), quote and escape it." This relies on the PHP function is_int(). In a standard web request (application/x-www-form-urlencoded), every input is a string. Even id=123 arrives as the string "123". In that scenario, is_int("123") returns false, and the value gets safely quoted as '123'.

But JSON is different. When json_decode processes {"id": 123}, it creates a native PHP integer. Now, is_int(123) returns true. The application sees this, says "Trust me, I'm an engineer," and concatenates the value directly into the SQL string without quotes. This removes the protective layer of quotes, exposing the database to Type Confusion attacks or logic manipulation via numeric literals.

The Code: The Smoking Gun

Let's look at the vulnerable pattern. The code likely resembled this simplified logic inside Nextmatch.php:

// VULNERABLE LOGIC
$filter = json_decode($json_input, true);
$query = "SELECT * FROM contacts WHERE ";
 
foreach ($filter as $col => $val) {
    // If it's a native integer, use it directly
    if (is_int($val)) {
        $query .= $col . " = " . $val;
    } else {
        // Otherwise, escape and quote it
        $query .= $col . " = '" . $db->quote($val) . "'";
    }
}

The fix implementation (released Jan 13, 2026) forces strict handling regardless of the input type. It stops treating integers as a special "safe" class that deserves unquoted access. The patch ensures that all user inputs, regardless of their JSON data type, are treated with suspicion and properly parameterized or cast explicitly before query construction.

// FIXED LOGIC
// Treat everything as a parameter or force quoting
$query .= $col . " = '" . $db->quote((string)$val) . "'";
// OR better yet, use prepared statements binding

The Exploit: Boolean Blindness

So how do we exploit this? We can't inject UNION SELECT because is_int() is strict—it won't accept strings containing SQL keywords. However, we can perform Database Type Juggling. This is where the PHP type juggling vulnerability hands the baton to the Database type juggling vulnerability.

Imagine a query filtering by a text column, like username. Normally, the query is WHERE username = 'admin'. If we inject a 0 via JSON ({"username": 0}), the query becomes WHERE username = 0 (no quotes).

In many database configurations (especially older MySQL/MariaDB versions or specific modes), comparing a string column to the integer 0 results in true for every row that doesn't start with a number. This effectively becomes WHERE true.

Attack Scenario:

  1. Recon: Attacker logs in and intercepts the Nextmatch filter request.
  2. Modify: They change a filter for a sensitive text column (e.g., status or owner) to the integer 0 or a boolean true (which PHP might cast to 1).
  3. Execute: The server generates SELECT * FROM secret_table WHERE owner = 0.
  4. Result: The database implicitly casts the owner string column to integers. Strings like "admin" become 0. The condition matches. The attacker sees data they shouldn't.

The Impact: Why Panic?

While this isn't a "drop table" RCE (Remote Code Execution) scenario immediately, the impact is severe for confidentiality and integrity. The vulnerability allows an authenticated user—potentially a low-privileged employee—to bypass horizontal privilege escalation controls.

By manipulating filters:

  • Data Leakage: A user could view calendar entries, contacts, or internal notes belonging to other users or administrators by nullifying the owner_id checks.
  • Logic Bypass: In workflows where status=1 means "Approved", injecting unquoted integers might allow manipulating workflow states if the application relies on string comparisons that are subverted by the injection.

The CVSS score of 8.7 reflects this high impact on confidentiality and integrity without requiring user interaction.

The Fix: Casting Calls

Mitigation is straightforward but requires code changes. You cannot fix this with a WAF easily because the payload ({"id": 0}) looks perfectly legitimate. The fix must occur in the PHP logic.

For Administrators: Update EGroupware immediately to version 23.1.20260113 or 26.0.20260113. If you are running an older version, you are exposed.

For Developers: Stop trusting is_int(), is_numeric(), or gettype() for security decisions regarding SQL generation. Always use Prepared Statements (parameterized queries). If you must build dynamic SQL strings (which you shouldn't), treat every input as hostile and ensure it is enclosed in quotes, regardless of whether PHP thinks it's a harmless integer.

Technical Appendix

CVSS Score
8.7/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Affected Systems

EGroupware Community EditionEGroupware EPL (Enterprise)

Affected Versions Detail

Product
Affected Versions
Fixed Version
EGroupware
EGroupware
< 23.1.2026011323.1.20260113
EGroupware
EGroupware
< 26.0.2026011326.0.20260113
AttributeDetail
CWE IDCWE-89 (SQL Injection)
Attack VectorNetwork (Authenticated)
CVSS Score8.7 (High)
Exploit StatusNo Public PoC
Root CausePHP Type Juggling / JSON Decoding
Affected ComponentNextmatch Filter Widget
CWE-89
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

Vulnerability Timeline

Vendor releases patched versions 23.1.20260113 and 26.0.20260113
2026-01-13
CVE-2026-22243 Published
2026-01-28
GHSA-rvxj-7f72-mhrx Released
2026-01-28

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.