Skip to content

CVE-2026-44901: Verified Reproduction

CVE-2026-44901: Wazuh cluster DAPI deserialization of untrusted data — RCE via sort casting builtin resolution getattr builtins, 'exec' in result merging

CVE-2026-44901 is verified against wazuh/wazuh · github. Affected versions: wazuh-manager >= 4.0.0, < 4.14.6 (all cluster-mode deployments). Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00325.

REPRO-2026-00325 wazuh/wazuh · github RCE Aug 23, 2026 CVE entry .txt
Severity
HIGH
Confidence
HIGH
Reproduced in
83m 25s
Tool calls
372
Spend
$19.46
01 · Overview

What Is CVE-2026-44901?

CVE-2026-44901 is a high-severity RCE vulnerability affecting wazuh/wazuh wazuh-manager >= 4.0.0, < 4.14.6 (all cluster-mode deployments). Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00325).

02 · Severity & CVSS

CVE-2026-44901 Severity

CVE-2026-44901 is rated high severity.

HIGH threat level
Weakness CWE-502 Deserialization of Untrusted Data — Deserialization of Untrusted Data

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected wazuh/wazuh Versions

wazuh/wazuh · github versions wazuh-manager >= 4.0.0, < 4.14.6 (all cluster-mode deployments) are affected.

How to Reproduce CVE-2026-44901

$ pruva-verify REPRO-2026-00325
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00325/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-44901

Remote code execution — 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

Fernet-framed malicious Wazuh worker dapi_res JSON with sort_casting=["exec"] and payload in affected_items[*].x

Attack chain
  1. TCP worker peer
  2. MasterHandler.execute(dapi_fwd)
  3. dapi.py json.loads(object_hook=as_wazuh_object)
  4. reduce(or_)
  5. results.py merge/getattr(builtins,'exec')
Runnable proof: reproduction_steps.sh
Captured evidence: master fixed 1master fixed 2product fixed 2worker fixed 1worker fixed 2
How the agent worked 814 events · 372 tool calls · 16h 44m
16h 44mDuration
372Tool calls
186Reasoning steps
814Events
27Dead-ends
Agent activity over 16h 44m
Policy
1
Support
9
Repro
488
Judge
75
Variant
236
0:001003:50

Root Cause and Exploit Chain for CVE-2026-44901

Versions: package/component: Wazuh manager cluster framework, specifically framework/wazuh/core/results.py as reached through framework/wazuh/core/cluster/dapi/dapi.py and the cluster TCP channel handled by wazuh.core.cluster.master.MasterHandler / wazuh.core.cluster.common.Handler.

CVE-2026-44901 is a Wazuh cluster Distributed API (DAPI) deserialization vulnerability in which a malicious or compromised worker node can return a crafted serialized AffectedItemsWazuhResult to a master node. In vulnerable code, AffectedItemsWazuhResult.decode_json() accepts attacker-controlled sort_casting values and merge() resolves those values with getattr(builtins, type_). A worker response containing sort_casting=["exec"] therefore makes the master call Python exec() on attacker-controlled item data while merging results from multiple nodes.

  • Affected package/component: Wazuh manager cluster framework, specifically framework/wazuh/core/results.py as reached through framework/wazuh/core/cluster/dapi/dapi.py and the cluster TCP channel handled by wazuh.core.cluster.master.MasterHandler / wazuh.core.cluster.common.Handler.
  • Affected versions: Wazuh manager cluster deployments before the fix commit b29849f8abb08d78f257e6106b6111a8a1b0e621 (reported as fixed in 4.14.6 and later). The reproduced vulnerable revision is the fixed commit parent: 24609e140155d7fd2bddd4ebb045dbde5bea320f.
  • Risk level and consequences: High. A malicious/compromised worker node, or an attacker with the shared cluster key able to act as a worker on the cluster channel, can cause code/command execution in the master-side Wazuh process when a distributed API response is merged.

Impact Parity

  • Disclosed/claimed maximum impact: Code execution on the Wazuh cluster master via the TCP/1516 Fernet-encrypted cluster channel and DAPI result merging.
  • Reproduced impact from this run: Code execution/command execution on the master-side Wazuh process. The payload executed via exec() and wrote unique marker files during both vulnerable attempts.
  • Parity: full
  • Not demonstrated: No additional privilege escalation beyond the privileges of the reproduced master-side process was claimed or required for this proof. The proof uses a minimally configured product cluster runtime rather than full Dockerized Wazuh service containers because Docker is unavailable in this environment, but it exercises the original Wazuh cluster TCP/Fernet framing, MasterHandler, DistributedAPI.forward_request, json.loads(..., object_hook=as_wazuh_object), and results.py merge sink.

Root Cause

The root cause is unsafe deserialization and later use of trusted-as-code type names from a worker-provided JSON result object.

In the vulnerable commit 24609e140155d7fd2bddd4ebb045dbde5bea320f:

  • AffectedItemsWazuhResult.decode_json() in framework/wazuh/core/results.py stores obj['sort_casting'] directly into the result object.
  • During DAPI result merging, AffectedItemsWazuhResult.__or__() calls merge(..., types=self.sort_casting).
  • merge() constructs casters using getattr(builtins, type_) without an allowlist.
  • _goes_before_than() applies each caster to sort values. If the worker supplied sort_casting=["exec"], the caster becomes Python built-in exec, and the corresponding item value is executed as Python source.

The product path that reaches this is:

  1. Master forwards a distributed request to the worker through MasterHandler.execute(command=b'dapi_fwd', ...).
  2. The worker response is delivered through the original Wazuh cluster string protocol (new_str, str_upd, dapi_res) over the Fernet-encrypted cluster TCP channel.
  3. dapi.py parses the worker response with json.loads(..., object_hook=c_common.as_wazuh_object).
  4. as_wazuh_object() calls AffectedItemsWazuhResult.decode_json().
  5. With more than one node response, dapi.py merges results using reduce(or_, response), triggering results.py sorting/casting.

The fix commit is:

  • b29849f8abb08d78f257e6106b6111a8a1b0e621

That commit adds validation in decode_json() and an explicit ALLOWED_CASTERS map in merge(), allowing only int, float, str, and bool. The same malicious sort_casting=["exec"] response is rejected with WazuhInternalError: Invalid sort_casting type 'exec'. Allowed types: bool, float, int, str.

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh.
  2. The script:
    • Reuses the prepared Wazuh repository at /pruva/project-cache/repo when available.
    • Resolves the fixed commit and vulnerable parent.
    • Verifies the vulnerable revision still contains getattr(builtins, type_) and the fixed revision contains the new sort_casting allowlist.
    • Creates worktrees for 24609e140155d7fd2bddd4ebb045dbde5bea320f and b29849f8abb08d78f257e6106b6111a8a1b0e621.
    • Starts a minimally configured real Wazuh master cluster TCP listener using wazuh.core.cluster.master.MasterHandler and wazuh.core.cluster.common.Handler with Fernet enabled. It binds to 127.0.0.1:1516 when available.
    • Connects a malicious worker peer over the original Wazuh cluster frame format, completes the encrypted hello handshake, receives the master b'dapi' forwarded request, and returns the crafted JSON through the original new_str / str_upd / dapi_res sequence.
    • Runs two vulnerable attempts and two fixed attempts.
  3. Expected evidence:
    • Vulnerable attempts produce repro/markers/product_marker_vuln_1.txt and repro/markers/product_marker_vuln_2.txt, each containing the unique marker value selected for that process.
    • Fixed attempts reach the same DAPI/object-hook path but reject sort_casting='exec' and do not create marker files.
    • bundle/repro/validation_verdict.json reports claim_outcome=confirmed, validated_surface=network_protocol, evidence_scope=production_path, and observed_impact_class=code_execution.

Evidence

Primary runtime artifacts are listed and digest-bound in bundle/repro/runtime_manifest.json.

Key files from the final successful run:

  • bundle/logs/product_patch_check.log — commit identity and patch absence/presence check.
  • bundle/logs/product_vuln_1.log and bundle/logs/product_vuln_2.log — vulnerable product-path attempts.
  • bundle/logs/product_fixed_1.log and bundle/logs/product_fixed_2.log — fixed negative-control attempts.
  • bundle/repro/observations/product_vuln_1.json and bundle/repro/observations/product_vuln_2.json — structured observations showing fernet_enabled=true, master_listened=true, dapi_forward_request_received_by_worker=true, worker_response_delivered_via_send_string=true, dapi_json_object_hook_path_reached=true, and marker_present=true.
  • bundle/repro/observations/product_fixed_1.json and bundle/repro/observations/product_fixed_2.json — structured observations showing the same network/DAPI path was reached but sort_casting_rejected=true and marker_present=false.
  • bundle/repro/markers/product_marker_vuln_1.txt and bundle/repro/markers/product_marker_vuln_2.txt — command-execution markers written by the vulnerable master-side process.
  • bundle/repro/evil_worker_response.json — the attacker-controlled worker JSON response containing sort_casting=["exec"] and the payload in affected_items[*].x.

Representative vulnerable evidence from the product logs:

  • MASTER_LISTENING original_wazuh_cluster_tcp=127.0.0.1:1516 ... fernet_key_len=32
  • WORKER_HELLO_ACCEPTED response=b'Client worker01 added'
  • WORKER_GOT_DAPI_REQUEST request_id=... json_len=550 ...
  • WORKER_SEND_STRING_UPDATED malicious JSON stored in master MasterHandler.in_str
  • WORKER_DAPI_RES_ACKNOWLEDGED master accepted dapi_res and released pending DistributedAPI request
  • MASTER_DAPI_RESULT type=AffectedItemsWazuhResult ... '_sort_casting': ['exec'] ...
  • MARKER_CHECK ... present=True content='CVE-2026-44901-vuln-...'

Representative fixed evidence:

  • MASTER_DAPI_RESULT type=WazuhInternalError ... "Invalid sort_casting type 'exec'. Allowed types: bool, float, int, str"
  • MARKER_CHECK ... present=False content=None

Environment details captured:

  • Repository URL: https://github.com/wazuh/wazuh.git
  • Vulnerable commit: 24609e140155d7fd2bddd4ebb045dbde5bea320f
  • Fixed commit: b29849f8abb08d78f257e6106b6111a8a1b0e621
  • Entrypoint: Wazuh cluster TCP peer (entrypoint_kind="tcp_peer")
  • Runtime stack: Wazuh MasterHandler, Wazuh Fernet frame Handler, Wazuh DistributedAPI.forward_request, Wazuh AffectedItemsWazuhResult.merge, Python 3

Recommendations / Next Steps

  • Use the fixed implementation from b29849f8abb08d78f257e6106b6111a8a1b0e621 or upgrade to Wazuh 4.14.6 or later.
  • Keep sort_casting validation both at deserialization time and at use time. Only safe, explicit caster names should be accepted.
  • Avoid resolving attacker-provided strings into arbitrary built-ins or callables.
  • Add regression tests that replay serialized worker AffectedItemsWazuhResult responses with invalid caster names such as exec, eval, open, and non-string/list values.
  • Consider hardening the DAPI worker-response trust boundary: even authenticated cluster workers should not be able to deserialize data structures that influence executable behavior on the master.

Additional Notes

  • The final bundle/repro/reproduction_steps.sh was executed twice consecutively and succeeded both times.
  • Each script run performs two vulnerable attempts and two fixed attempts with fresh per-process marker values.
  • Docker was not available in this environment, so the proof uses a minimally configured local Wazuh cluster runtime from the real source tree rather than Wazuh container images. It does not reimplement the vulnerable sink or the cluster frame protocol; it imports and executes the real Wazuh modules for the master TCP listener, Fernet framing, DAPI forwarding, JSON object hook, and result merging.
  • The reproduction is self-contained: it creates runtime helper code and payload files at execution time, installs Python dependencies into bundle/repro/venv if needed, and writes all proof diagnostics under bundle/logs/ and bundle/repro/.

CVE-2026-44901 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:001:44
0:00
session startedaccounts/fireworks/models/kimi-k3 · CVE-2026-44901 · REPRO-20
0:07
0:08
0:10
web search
0:52
0:52
extract_facts
no facts extracted
0:54
0:54
supportclaim_contract
1:00
1:00
1:00
1:05
1:05
1:05
1:05
1:09
1:09
1:12
1:12
1:22
$python3 --version; ls /usr/bin/python*; pip3 --version 2>/dev/null; df -h /workspace | tail -1; nproc; free -m | head -2
0.6s
Python 3.14.4
/usr/bin/python
/usr/bin/python3
/usr/bin/python3-config
/usr/bin/python3.14
/usr/bin/python3.14-config
pip 25.1.1 from /usr/lib/python3/dist-packages/pip (python 3.14)
tmpfs           4.0G   40K  4.0G   1% /workspace
1:23
1:32
1:33
web search
1:41
1:44
$cd /pruva/project-cache/repo-mirrors/wazuh.git && git tag -l 'v4.14.*' | sort -V && git log --oneline -1
0.4s
v4.14.0
v4.14.1
v4.14.2
v4.14.3
v4.14.4
v4.14.5
v4.14.6
v4.14.7
08 · How to Fix

How to Fix CVE-2026-44901

Coming soon

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

10 · FAQ

FAQ: CVE-2026-44901

Is CVE-2026-44901 exploitable?

Yes. Pruva independently reproduced CVE-2026-44901 in wazuh/wazuh 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-00325).

How severe is CVE-2026-44901?

CVE-2026-44901 is rated high severity.

What type of vulnerability is CVE-2026-44901?

CVE-2026-44901 is classified as CWE-502 Deserialization of Untrusted Data (Deserialization of Untrusted Data), a RCE vulnerability.

Which versions of wazuh/wazuh are affected by CVE-2026-44901?

wazuh/wazuh wazuh-manager >= 4.0.0, < 4.14.6 (all cluster-mode deployments) is affected by CVE-2026-44901.

How can I reproduce CVE-2026-44901?

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-44901 reproduction verified?

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

References for CVE-2026-44901

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