Skip to content

CVE-2026-23921: Verified Reproduction

CVE-2026-23921: Blind SQL injection in Zabbix API CApiService.php via the sortfield parameter allows low-privileged API users to exfiltrate database data and potentially compromise administrator accounts.

CVE-2026-23921 is verified against zabbix/zabbix · unknown. Affected versions: 7.0.0 through 7.0.21, 7.2.0 through 7.2.14, and 7.4.0 through 7.4.5. Vulnerability class: SQLi. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00360.

REPRO-2026-00360 zabbix/zabbix · unknown SQLi Sep 24, 2026 CVE entry .txt
Severity
HIGH
CVSS
8.7
Confidence
HIGH
Reproduced in
38m 26s
Tool calls
265
Spend
$6.82
01 · Overview

What Is CVE-2026-23921?

CVE-2026-23921 is a high-severity SQLi vulnerability affecting zabbix/zabbix 7.0.0 through 7.0.21, 7.2.0 through 7.2.14, and 7.4.0 through 7.4.5.. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00360).

02 · Severity & CVSS

CVE-2026-23921 Severity & CVSS Score

CVE-2026-23921 is rated high severity, with a CVSS base score of 8.7 out of 10.

HIGH threat level
8.7 / 10 CVSS base
Weakness CWE-89 — Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected zabbix/zabbix Versions

zabbix/zabbix · unknown versions 7.0.0 through 7.0.21, 7.2.0 through 7.2.14, and 7.4.0 through 7.4.5. are affected.

How to Reproduce CVE-2026-23921

$ pruva-verify REPRO-2026-00360
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00360/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-23921

SQL injection — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

Authenticated JSON-RPC host.get params groupBy and sortfield containing the same MySQL conditional SLEEP expression

Attack chain
  1. POST /api_jsonrpc.php
  2. host.get
  3. CApiService::applyQuerySortOptions()
  4. ORDER BY
How the agent worked 438 events · 265 tool calls · 38 min
38 minDuration
265Tool calls
56Reasoning steps
438Events
20Dead-ends
Agent activity over 38 min
Policy
1
Support
14
Repro
188
Judge
38
Variant
192
Verify
1
0:0038:06

Root Cause and Exploit Chain for CVE-2026-23921

Versions: component: Zabbix JSON-RPC API query generation in ui/include/classes/api/CApiService.php, reached through legacy services such as host.get.Fixed: releases are 7.0.22, 7.2.15, and 7.4.6.

CVE-2026-23921 is an authenticated blind SQL injection in the Zabbix JSON-RPC API. In Zabbix 7.0.21, legacy *.get methods preserve a caller-supplied groupBy option and CApiService::applyQuerySortOptions() uses that option as the allowlist for sortfield. Repeating an SQL expression in both fields makes validation approve attacker input, after which applyQuerySortField() places it into ORDER BY. This run confirmed the vulnerability through the real /api_jsonrpc.php endpoint as a newly created low-privileged User-role account. Matched predicates produced a repeatable timing oracle on 7.0.21, while 7.0.22 rejected the same requests.

  • Affected component: Zabbix JSON-RPC API query generation in ui/include/classes/api/CApiService.php, reached through legacy services such as host.get.
  • Affected versions: 7.0.0–7.0.21, 7.2.0–7.2.14, and 7.4.0–7.4.5. Vendor-fixed releases are 7.0.22, 7.2.15, and 7.4.6.
  • Risk level: High (vendor CVSS 4.0 score 8.7).
  • Consequences: Any low-privileged account with API access can use true/false SQL predicates and database delay functions to infer arbitrary database values. The disclosed consequences include session identifier disclosure and subsequent administrator-account compromise.

Impact Parity

  • Disclosed/claimed maximum impact: Authenticated remote blind SQL injection capable of arbitrary database-data exfiltration.
  • Reproduced impact from this run: Authenticated low-privilege remote SQL injection through /api_jsonrpc.php, demonstrated by three successful true predicates invoking SLEEP(2) and three false predicates returning immediately. Because one host.get query sorts two rows, each true probe delayed by approximately four seconds. The predicate operated on version(), demonstrating database-expression evaluation and the binary timing channel used for extraction.
  • Parity: full.
  • Not demonstrated: The run did not dump session identifiers or take over an administrator account; doing so is unnecessary once attacker-selected database predicates and the repeatable timing oracle are established.

Root Cause

The vulnerable 7.0.21 implementation computes:

$sort_columns = $group_by
    ? array_merge($options['groupBy'], $aggregate_sort_columns)
    : $this->sortColumns;

The JSON-RPC caller controls options['groupBy']. An attacker therefore sends a legitimate field and an SQL expression in groupBy, then repeats the expression in sortfield. str_in_array() accepts the sort value because the application itself copied that value into sort_columns. applyQuerySortField() subsequently concatenates the accepted string into the SQL ORDER BY clause. countOutput: true and the legitimate hostid group field keep the rest of the generated aggregate query valid.

Zabbix 7.0.22 instead derives the allowed fields from the server-owned schema list:

$allowed_sort_fields = array_intersect($this->sortColumns, $options['groupBy']);

It also verifies groupBy is an array and each grouped field is a string. The first corrective commit in the release history is 0dced774b07bb542885787d82ddbdfe26ea782ee; subsequent DEV-4649 commits refactored the released validation. The tested release identities were source tags 7.0.21 (6542e21fe6adc8ab7bf903c2a9d1df80e80366cd) and 7.0.22 (584690ce104180ead8cd5d4d963a02b5e59bbeed). The primary runtime targets were official immutable Zabbix image digests recorded in runtime_manifest.json.

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh from any working directory. Docker, curl, jq, Python 3, sha256sum, and timeout must be available.
  2. The script pulls immutable official image digests, starts isolated MySQL, Zabbix server, and Zabbix web/API containers for 7.0.21, and waits for a successful apiinfo.version JSON-RPC health check.
  3. It logs in as bootstrap Admin only for setup, creates a normal User-role account with read-only access to the default host group, and confirms that account can perform a baseline host.get.
  4. Through that low-privileged account, it interleaves three false and three true version() timing predicates in attacker-controlled groupBy and sortfield values sent to /api_jsonrpc.php.
  5. It destroys the vulnerable stack and repeats the exact procedure against an isolated 7.0.22 stack as the fixed negative control.
  6. It succeeds only if 7.0.21 has a median timing delta of at least 1.5 seconds and 7.0.22 either rejects every malicious request or has no timing oracle. It then writes bundle/repro/runtime_manifest.json with immutable artifact hashes.

Expected final output includes:

"vulnerable_timing_oracle": true
"fixed_negative_control": true
"confirmed": true
CONFIRMED: CVE-2026-23921 blind SQL injection reached through the real Zabbix API endpoint.

Evidence

  • Final machine verdict: bundle/repro/proof/verdict.json
    • Vulnerable timing delta: 3.973936498980038 seconds.
    • Fixed timing delta: -0.0018714029574766755 seconds.
    • vulnerable_timing_oracle=true, fixed_rejected_payload=true, confirmed=true.
  • Vulnerable endpoint transcript: bundle/repro/proof/vulnerable/timing_results.json
    • False median: 0.04664472601143643 seconds.
    • True median: 4.020581224991474 seconds.
    • Every false and true request returned a valid JSON-RPC result, showing that the vulnerable service executed rather than rejected the values.
  • Fixed negative control: bundle/repro/proof/fixed/timing_results.json
    • All six requests returned JSON-RPC error -32500: Sorting by field ... not allowed.
    • No sleep-dependent timing difference remained.
  • Attacker requests: bundle/repro/proof/vulnerable/false_probe_request.json and true_probe_request.json contain the exact redacted request bodies. The session token is intentionally omitted.
  • Low-privilege precondition: bundle/repro/proof/vulnerable/low_user_identity.json records type: 1 and role ID 1; attempt_metadata.json records read-only permission over host group ID 4. baseline_response.json confirms the account could read a host.
  • Product and target identity: product_version.txt, loaded_component.txt, and container_identity.json in each role directory bind the run to real Zabbix 7.0.21 and 7.0.22 web products and the loaded CApiService.php. OCI labels identify the vulnerable image as 7.0.21, and the runtime manifest binds it to digest sha256:0282eba8bac999284672e3abaefbdd28b16026bae6a1673a7f560f35eb2c712d.
  • Runtime logs: web_final.log and server_final.log in each proof directory show the actual HTTP JSON-RPC requests and product startup. bundle/logs/reproduction_steps.log remains diagnostic and is deliberately not hash-bound while the script is writing it.
  • Runtime manifest: bundle/repro/runtime_manifest.json has entrypoint_kind=endpoint, service_started=true, healthcheck_passed=true, target_path_reached=true, and hashes all 22 finalized proof artifacts.

Recommendations / Next Steps

  1. Upgrade to Zabbix 7.0.22, 7.2.15, 7.4.6, or a later supported release.
  2. Never derive identifier allowlists from request values. Intersect requested fields with a server-owned allowlist and map accepted logical names to known SQL identifiers.
  3. Enforce strict array/string schemas before all query-construction branches and reject undeclared options rather than preserving them through generic array merging.
  4. Add regression tests for duplicate groupBy/sortfield expressions, scalar/object type confusion, aggregate aliases, and every legacy service sharing CApiService.
  5. Until upgraded, restrict API access, revoke unnecessary API-capable accounts/tokens, and review web/database logs for SLEEP, pg_sleep, CASE, IF, substring, or unusual comma-containing sort fields.

Additional Notes

  • The final script revision completed successfully with immutable image digests. Two prior clean full runs also passed, with vulnerable median deltas of 3.9988 and 3.9967 seconds and fixed controls showing no timing oracle.
  • Every stack uses a fresh MySQL database, private Docker network, unique container names, and teardown, so vulnerable and fixed observations cannot contaminate each other.
  • Timing predicates are intentionally bounded to six probes per target and inspect only the first character of version(); the script does not extract sensitive data.
  • Docker reported rootless operation without cgroups, but that did not affect service health, endpoint reachability, timing separation, isolation, or cleanup.

CVE-2026-23921 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:58
0:00
session startedgpt-5.6-sol · CVE-2026-23921 · REPRO-20
0:08
0:09
0:11
web search
0:26
web search
0:28
web search
0:30
web search
0:38
1:23
extract_facts
no facts extracted
1:29
1:31
1:31
supportclaim_contract
1:41
1:41
1:42
1:42
1:42
1:54
1:54
1:55
1:57
web search
2:16
2:17
# Root Cause Analysis: CVE-2026-23921

## Summary

Zabbix API versions before the vendor fixes accept attacker-controlled `groupBy` values into the whitelist used to validate `sortfield`. A low-privileged authenticated API caller can repeat an SQL expression in both fields, causing it to be concatenated into the generated `ORDER BY` clause. This report will be finalized with current-run endpoint timing evidence after execution of `bundle/repro/reproduction_steps.sh`.

## Impact

- **Affected component:** Zabbix JSON-RPC API, particularly `ui/include/classes/api/CApiService.php` and legacy API methods such as `host.get`.
- **Affected versions:** 7.0.0 through 7.0.21, 7.2.0 through 7.2.14, and 7.4.0 through 7.4.5.
- **Risk:** High. An authenticated low-privileged API user can perform blind SQL injection and infer arbitrary database values, potentially disclosing sessions and enabling administrator account compromise.

## Impact Parity

- **Disclosed/claimed maximum impact:** Authenticated remote blind SQL injection with arbitrary database-data exfiltration.
- **Reproduced impact from this run:** Pending current-run runtime validation.
- **Parity:** none (pending execution).
- **Not d… [truncated]
2:30
$set -eu printf '%s\n' '--- docker ---' command -v docker || true docker ps 2>&1 || true printf '%s\n' '--- cache ---' find /pruva/project-cache -maxdepth 2 -mindepth 1 -print 2>/dev/null | sort | head -100 printf '%s\n' '--- host ---' uname -a id
0.7s
--- docker ---
/usr/bin/docker
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
--- cache ---
/pruva/project-cache/.pruva
/pruva/project-cache/.pruva/cache_manifest.json
/pruva/project-cache/repo-mirrors
--- host ---
2:56
2:57
2:58
08 · How to Fix

How to Fix CVE-2026-23921

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-23921 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-23921

Is CVE-2026-23921 exploitable?

Yes. Pruva independently reproduced CVE-2026-23921 in zabbix/zabbix and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00360).

How severe is CVE-2026-23921?

CVE-2026-23921 is rated high severity, with a CVSS score of 8.7 out of 10.

What type of vulnerability is CVE-2026-23921?

CVE-2026-23921 is classified as CWE-89 (Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')), a SQLi vulnerability.

Which versions of zabbix/zabbix are affected by CVE-2026-23921?

zabbix/zabbix 7.0.0 through 7.0.21, 7.2.0 through 7.2.14, and 7.4.0 through 7.4.5. is affected by CVE-2026-23921.

How can I reproduce CVE-2026-23921?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-23921 reproduction verified?

Yes. Pruva reproduced CVE-2026-23921 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for CVE-2026-23921

Authoritative sources for CVE-2026-23921 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.