← Back to CVE List
CVE-2026-74043NVD
Vulnerability Summary
Summary The `@expose_resources` framework upload helpers `upload_list_file`, `upload_rule_file`, and `upload_decoder_file` build their destination path from a caller-supplied `filename` with no traversal validation of their own — that check exists only in the REST API controller. They are reachable two ways: path 1 REST controller (validates filename) -> upload helper path 2 as_wazuh_object -> DistributedAPI(request) -> upload helper => path 2 skips the controller; filename is never validated A malicious/compromised cluster worker uses the second path: it hands the master a DAPI request whose `f` is one of these exposed helpers and whose `f_kwargs.filename` contains `../` traversal, producing an arbitrary-path file write as the `wazuh` user. This is the same primitive CVE-2026-25770 fixed for the cluster file-sync path (`receive_file`), reached via a code path that fix did not cover. The upload helpers did not become newly exposed; they became reachable through a path that bypasses the validation layer they implicitly relied upon. Details 1. The exposed-resource gate (`_wazuh_exposed_`, #34889) lets the `as_wazuh_object` callable branch resolve these functions — they are legitimately `@expose_resources`-decorated. The gate checks the callable's identity, not its arguments. 2. No framework-layer `filename` validation: - `cdb_list.upload_list_file`: the only guard, `get_filenames_paths([filename])[0] != full_path`, is ineffective — `get_filenames_paths` uses `Path(root).rglob(filename)`, which never matches a `../` pattern, so it returns the default (== `full_path`) and the `!=` guard never fires. - `rule.upload_rule_file` / `decoder.upload_decoder_file`: `validate_upload_delete_dir` validates only `relative_dirname`; `filename` is unvalidated. `full_path = join(WAZUH_PATH, relative_dirname, filename)`. 3. `upload_file` delegates to `safe_move` (core/utils.py), a generic move utility that assumes the destination was already validated by the caller (no bounds check of its own). 4. Traversal protection lives only in the REST controller format validators (`_cdb_filename_path = ^[\-\w]+$`, `_xml_filename_path = ^[\w\-]+\.xml$`, `api/validator.py:41-42`), which the callable branch never invokes. 5. On the master, `APIRequestQueue.run` (dapi.py:714-720) does `request = json.loads(request, object_hook=as_wazuh_object)` then `DistributedAPI(request, ...)` — the wire request dict is splatted in with no re-verification of `rbac_permissions` against the sending node. A request that omits `rbac_permissions` inherits the constructor default `{'rbac_mode':'black'}` (dapi.py:111), which permits the action. Reachability: (a) `as_wazuh_object` resolves these functions as `f` (they pass the exposed-resource gate); (b) `run_local` calls `f(**f_kwargs)` with attacker `f_kwargs` (dapi.py:243-246, the same execution point as CVE-2026-25769); (c) the function performs the unvalidated-`filename` write. Only the Fernet-socket transport (the same one already demonstrated by CVE-2026-25769) was intentionally left out of scope. PoC Isolated 2-node lab, two `wazuh/wazuh-manager:4.14.6`, shared cluster key. The PoC invokes the framework upload functions directly in-process to demonstrate the write primitive (rbac harness set to the `DistributedAPI` default `{'rbac_mode':'black'}`, not a privileged assumption). Harmless target inside the container's `/var/tmp`. Rule (well-formed XML content): ``` upload_rule_file(relative_dirname="etc/rules", filename="../../../tmp/POC_RULE_TRAVERSAL", content='<group name="poc_traversal"></group>', overwrite=True) -> writes /var/tmp/POC_RULE_TRAVERSAL (outside /var/ossec); result "Rule was successfully uploaded" ``` CDB (content-constrained): ``` upload_list_file(filename="../../../tmp/POC_CDB_TRAVERSAL", content="poc:cdb_traversal_write_proof", overwrite=True) -> writes /var/tmp/POC_CDB_TRAVERSAL ``` Guard-bypass shown independently: `get_filenames_paths(["../.."])[0] == join(USER_LISTS_PATH, "../..")`. Full reproduction transcript and scripts attached. Source-diff scoping the novelty (v4.14.2 vulnerable -> v4.14.6): the CVE-2026-25770 fix changed only `receive_file` (4.14.2 `open(common.WAZUH_PATH + data.decode(), 'wb')` -> 4.14.6 `realpath` + `_ALLOWED_PREFIXES`); `upload_list_file`, `upload_rule_file`, `validate_upload_delete_dir`, `safe_move`, and `upload_file` are byte-identical between v4.14.2 and v4.14.6 — i.e. never touched by that fix. Still source-identical on the 4.14.8 dev branch. Impact Arbitrary-path file write as the `wazuh` service user, reachable by a compromised/malicious cluster worker (requires the cluster key — same trust model as CVE-2026-25769/25770). Demonstrated: the write primitive (High). Not developed (deliberate scope limit): chaining `upload_rule_file` with `filename="../../etc/ossec.conf"` + valid XML to inject a `<localfile>` command block executed as root by `wazuh-logcollector` — i.e. the CVE-2026-25770 root-RCE outcome, via this different, still-unpatched code path. Suggested fix Validate `filename` at the framework layer in the three upload functions: resolve the final destination and verify it stays beneath the intended base directory before moving the file — the same `realpath` + `_ALLOWED_PREFIXES` commonpath check the CVE-2026-25770 fix already added to `receive_file` (common.py:1000-1002). Do not rely on controller-layer format validation for functions reachable via the cluster callable path. Scope / disclosure note This report demonstrates the file-write primitive only; the ossec.conf->root-RCE chain and the Fernet-socket network delivery were intentionally not developed (responsible disclosure). Offered framing: an incomplete fix / variant of CVE-2026-25770 — new-CVE vs 25770-variant is left to the vendor. poc_rule_traversal.py test_cdb_guard.py verify_25769.py verify_layer2.py evidence-run.txt poc_cdb_traversal.py
CVSS v3.1 Base Metrics — Score 9.0
Attack VectorNetwork
Attack ComplexityLow
Privileges RequiredHigh
User InteractionNone
ScopeChanged
ConfidentialityLow
IntegrityHigh
AvailabilityHigh
Affected & Patched Versions
- wazuh-manager >= 4.1.1
- wazuh-manager 4.14.8