CVE-2025-24016: Unsafe Deserialization Vulnerability in Wazuh Leading to Remote Code Execution

Executive Summary

CVE-2025-24016 is a critical remote code execution (RCE) vulnerability affecting Wazuh, a widely used open-source security information and event management (SIEM) platform. This vulnerability stems from unsafe deserialization of DistributedAPI (DAPI) parameters, allowing an attacker with API access to execute arbitrary Python code on the Wazuh server. Specifically, versions 4.4.0 to 4.9.0 are affected. The vulnerability is triggered when an attacker injects a malicious dictionary into a DAPI request or response, which is then processed by the as_wazuh_object function. Successful exploitation can lead to complete system compromise, including data theft, service disruption, and further lateral movement within the network. The vulnerability has been patched in version 4.9.1 by replacing the unsafe eval function with ast.literal_eval.

Technical Details

  • CVE ID: CVE-2025-24016
  • Affected Software: Wazuh
  • Affected Versions: 4.4.0 to 4.9.0
  • Vulnerability Type: Unsafe Deserialization
  • CVSSv3.1 Score: 9.9 (Critical)
  • CVSSv3.1 Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:H
  • Affected Component: framework/wazuh/core/cluster/common.py - as_wazuh_object function
  • Attack Vector: Network
  • Privileges Required: Low (API Access)
  • User Interaction: None
  • Scope: Changed

The vulnerability resides in the as_wazuh_object function within the framework/wazuh/core/cluster/common.py file. This function is responsible for deserializing JSON data received through the Distributed API. The DAPI is used for communication between Wazuh components, including the Wazuh API, manager, and agents.

The vulnerable code snippet is as follows (prior to the patch):

def as_wazuh_object(dct: Dict):
    try:
        if '__wazuh_datetime__' in dct:
            return datetime.datetime.fromisoformat(dct['__wazuh_datetime__'])
        elif '__unhandled_exc__' in dct:
            exc_data = dct['__unhandled_exc__']
            return eval(exc_data['__class__'])(*exc_data['__args__'])
        return dct

    except (KeyError, AttributeError):
        return dct

This code checks if the dictionary dct contains the key __unhandled_exc__. If it does, it retrieves the __class__ and __args__ values from the dictionary and uses the eval function to create an instance of the specified class with the provided arguments. The use of eval on attacker-controlled data is inherently dangerous, as it allows arbitrary Python code execution.

Root Cause Analysis

The root cause of CVE-2025-24016 is the use of the eval function to deserialize data received from the Distributed API. The eval function executes arbitrary Python code, making it a prime target for exploitation. An attacker can craft a malicious JSON payload containing a specially crafted dictionary with the __unhandled_exc__ key. This dictionary specifies a Python class and arguments that, when evaluated by eval, execute arbitrary code.

The vulnerability is triggered because the as_wazuh_object function does not properly sanitize or validate the input data before passing it to eval. This allows an attacker to inject arbitrary code into the __class__ and __args__ fields, leading to remote code execution.

For example, an attacker could send the following JSON payload:

{
    "__unhandled_exc__": {
        "__class__": "os.system",
        "__args__": ["touch /tmp/pwned"]
    }
}

When this payload is processed by the as_wazuh_object function, the eval function will execute os.system("touch /tmp/pwned"), creating a file named /tmp/pwned on the Wazuh server.

The vulnerability is particularly dangerous because it can be triggered by anyone with API access. This includes compromised dashboards, Wazuh servers within the cluster, and, in certain configurations, even compromised agents. The DAPI is designed for internal communication between Wazuh components, but the lack of proper input validation makes it vulnerable to exploitation.

Patch Analysis

The vulnerability was patched in Wazuh version 4.9.1. The patch replaces the unsafe eval function with ast.literal_eval. The ast.literal_eval function safely evaluates a string containing a Python literal (e.g., a string, number, tuple, list, dict, boolean, or None). It does not execute arbitrary code, mitigating the RCE vulnerability.

Here's the relevant code change in framework/wazuh/core/cluster/common.py:

--- a/framework/wazuh/core/cluster/common.py
+++ b/framework/wazuh/core/cluster/common.py
@@ -1824,7 +1825,8 @@ def as_wazuh_object(dct: Dict):
             return datetime.datetime.fromisoformat(dct['__wazuh_datetime__'])
         elif '__unhandled_exc__' in dct:
             exc_data = dct['__unhandled_exc__']
-            return eval(exc_data['__class__'])(*exc_data['__args__'])
+            exc_dict = {exc_data['__class__']: exc_data['__args__']}
+            return ast.literal_eval(json.dumps(exc_dict))
         return dct
 
     except (KeyError, AttributeError):

Line-by-line explanation:

  • - return eval(exc_data['__class__'])(*exc_data['__args__']): This line contains the vulnerable code. It uses eval to execute arbitrary code based on the __class__ and __args__ values in the exc_data dictionary.
  • + exc_dict = {exc_data['__class__']: exc_data['__args__']}: This line creates a dictionary with the class name as the key and the arguments as the value.
  • + return ast.literal_eval(json.dumps(exc_dict)): This line converts the dictionary to a JSON string and then uses ast.literal_eval to safely evaluate it. This prevents arbitrary code execution.

The patch effectively mitigates the vulnerability by replacing the unsafe eval function with the safe ast.literal_eval function. The ast.literal_eval function only evaluates Python literals, preventing attackers from injecting arbitrary code.

Additionally, the following changes were made in api/test/integration/tavern_utils.py:

--- a/api/test/integration/tavern_utils.py
+++ b/api/test/integration/tavern_utils.py
@@ -2,7 +2,7 @@
 # Created by Wazuh, Inc. <info@wazuh.com>.
 # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2
 
--
+import ast
 import json
 import re
 import subprocess
@@ -238,7 +238,7 @@ def test_validate_data_dict_field(response, fields_dict):
 
         for element in field_list:
             try:
-                assert (isinstance(element[key], eval(value)) for key, value in dikt.items())
+                assert (isinstance(element[key], ast.literal_eval(value)) for key, value in dikt.items())
             except KeyError:
                 assert len(element) == 1
                 assert isinstance(element['count'], int)
@@ -486,7 +486,7 @@ def check_agent_active_status(agents_list):
             raise subprocess.SubprocessError("Error while trying to get agents") from exc
 
         # Transform string representation of list to list and save agents id
-        id_active_agents = [agent['id'] for agent in eval(output)]
+        id_active_agents = [agent['id'] for agent in ast.literal_eval(output)]
 
         if all(a in id_active_agents for a in agents_list):
             break

These changes replace eval with ast.literal_eval in the test suite, ensuring that the tests are also using the safe evaluation method.

Finally, the following changes were made in framework/wazuh/core/cluster/tests/test_common.py:

--- a/framework/wazuh/core/cluster/tests/test_common.py
+++ b/framework/wazuh/core/cluster/tests/test_common.py
@@ -1749,7 +1749,11 @@ def test_as_wazuh_object_ok():
     # Test the fifth condition
     result = cluster_common.as_wazuh_object({'__unhandled_exc__': {'__class__': 'ValueError',
                                                                    '__args__': ('test',)}})
-    assert isinstance(result, ValueError)
+    assert result == {"ValueError": ["test"]}
+
+    result = cluster_common.as_wazuh_object({'__unhandled_exc__': {'__class__': 'exit',
+                                                                   '__args__': []}})
+    assert result == {"exit": []}
 
     # No condition fulfilled
     assert isinstance(cluster_common.as_wazuh_object({"__wazuh_datetime_bad__": "2021-10-14"}), dict)

These changes update the tests to reflect the new behavior of as_wazuh_object after the patch. Instead of returning a ValueError or exiting, the function now returns a dictionary representing the exception.

Exploitation Techniques

An attacker can exploit CVE-2025-24016 by sending a malicious JSON payload to the Wazuh server through the API. The payload must contain the __unhandled_exc__ key, along with the __class__ and __args__ values that specify the code to be executed.

Proof of Concept (PoC):

The following PoC demonstrates how to exploit the vulnerability using the run_as endpoint:

curl -X POST -k -u "wazuh-wui:MyS3cr37P450r.*-" -H "Content-Type: application/json" --data '{"__unhandled_exc__":{"__class__": "os.system", "__args__": ["touch /tmp/pwned"]}}' https://<worker-server>:55000/security/user/authenticate/run_as

Explanation:

  • -X POST: Specifies the HTTP method as POST.
  • -k: Disables SSL certificate verification (for testing purposes).
  • -u "wazuh-wui:MyS3cr37P450r.*-": Provides the username and password for authentication. The default credentials are used here.
  • -H "Content-Type: application/json": Sets the Content-Type header to application/json.
  • --data '{"__unhandled_exc__":{"__class__": "os.system", "__args__": ["touch /tmp/pwned"]}}': Specifies the malicious JSON payload. This payload will execute the command touch /tmp/pwned on the Wazuh server.
  • https://<worker-server>:55000/security/user/authenticate/run_as: Specifies the URL of the run_as endpoint. Replace <worker-server> with the actual hostname or IP address of the Wazuh worker server.

Attack Scenario:

  1. Reconnaissance: The attacker identifies a vulnerable Wazuh server (version 4.4.0 to 4.9.0).
  2. Authentication: The attacker obtains valid API credentials, either through default credentials, credential stuffing, or other means.
  3. Payload Injection: The attacker sends a malicious JSON payload to the run_as endpoint, as shown in the PoC above.
  4. Code Execution: The as_wazuh_object function deserializes the payload and executes the attacker-controlled code.
  5. System Compromise: The attacker gains control of the Wazuh server and can perform various malicious activities, such as data theft, service disruption, or lateral movement within the network.

Real-World Impact:

The exploitation of CVE-2025-24016 can have severe consequences for organizations using Wazuh. An attacker could:

  • Gain complete control of the Wazuh server: This allows the attacker to access sensitive data, modify configurations, and install malware.
  • Compromise the entire Wazuh cluster: If the attacker gains control of the master server, they can potentially compromise all other servers and agents in the cluster.
  • Disrupt security monitoring: The attacker can disable or tamper with Wazuh's monitoring capabilities, allowing them to carry out further attacks undetected.
  • Steal sensitive data: The attacker can access logs, alerts, and other sensitive data stored on the Wazuh server.
  • Use the Wazuh server as a launching pad for further attacks: The attacker can use the compromised server to attack other systems within the network.

Mitigation Strategies

To mitigate the risk of CVE-2025-24016, organizations should take the following steps:

  1. Upgrade to Wazuh version 4.9.1 or later: This version contains the patch that fixes the vulnerability.
  2. Restrict API access: Limit API access to only authorized users and systems.
  3. Implement strong authentication: Use strong passwords and multi-factor authentication to protect API credentials.
  4. Monitor API traffic: Monitor API traffic for suspicious activity, such as unexpected requests or large data transfers.
  5. Regularly review and update security configurations: Ensure that Wazuh's security configurations are up-to-date and properly configured.
  6. Implement network segmentation: Segment the network to limit the impact of a successful attack.
  7. Use a Web Application Firewall (WAF): A WAF can help to detect and block malicious requests before they reach the Wazuh server.

Configuration Changes:

  • Review and restrict access to the Wazuh API. Ensure that only authorized users and systems have access.
  • Implement strong authentication mechanisms for the API, such as multi-factor authentication.
  • Monitor API logs for suspicious activity.

Security Best Practices:

  • Follow the principle of least privilege when granting API access.
  • Regularly review and update security configurations.
  • Implement a robust security monitoring program.
  • Stay informed about the latest security threats and vulnerabilities.

Alternative Solutions:

If upgrading to Wazuh version 4.9.1 is not immediately possible, consider the following temporary workarounds:

  • Implement a WAF rule to block malicious payloads: A WAF rule can be created to detect and block requests containing the __unhandled_exc__ key. However, this workaround is not foolproof, as attackers may be able to bypass the rule with obfuscation techniques.
  • Disable the run_as endpoint: If the run_as endpoint is not required, it can be disabled to prevent exploitation of the vulnerability through this specific endpoint. However, this does not address the underlying vulnerability in the as_wazuh_object function.

Timeline of Discovery and Disclosure

  • Vulnerability Discovered: Unknown
  • Vulnerability Reported: Unknown
  • Patch Released: July 23, 2024 (Wazuh 4.9.1)
  • Public Disclosure: February 10, 2025

References

Comparative Analysis

CVE-2025-24016 is similar to other unsafe deserialization vulnerabilities that have been discovered in various software applications. These vulnerabilities typically arise when an application deserializes data from an untrusted source without proper validation, allowing an attacker to inject malicious code into the deserialized object.

A well-known example is the Apache Struts vulnerability (CVE-2017-5638), which allowed remote code execution through the Content-Type header. Similar to CVE-2025-24016, the Apache Struts vulnerability was caused by the application's failure to properly sanitize user-supplied input before deserialization.

The evolution of security practices has led to increased awareness of the risks associated with deserialization. Modern frameworks and libraries often provide built-in mechanisms for safe deserialization, such as whitelisting allowed classes or using secure serialization formats. However, as CVE-2025-24016 demonstrates, vulnerabilities can still arise when developers fail to use these mechanisms properly or when custom deserialization logic is implemented without adequate security considerations.

The key takeaway from these vulnerabilities is the importance of input validation and secure coding practices. Applications should never trust data from untrusted sources and should always sanitize and validate input before processing it. Developers should also be aware of the risks associated with deserialization and should use secure deserialization techniques whenever possible.

Read more