← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-28795 · CWE-22 · Disclosed 2026-03-06

OpenChatBI is an intelligent chat-based BI tool powered by large language models

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is less a front-door master key than a badly placed mail slot that only matters if the room behind it is already reachable

CVE-2026-28795 is a path traversal flaw in OpenChatBI's save_report tool. In vulnerable builds <=0.2.1, the code strips only leading dots from the file_format parameter and then concatenates it into the output filename, so values like /../../... can escape the intended report directory and turn a report export into an arbitrary file write. The fix landed in 0.2.2, which replaced the weak sanitization with a strict allowlist of supported extensions.

The vendor-style 9.8 framing overstates real enterprise risk. The primitive is dangerous, but the exploitable population is much smaller than a generic pre-auth internet service: this is a niche self-hosted BI/LLM app, many deployments are internal, and turning arbitrary file write into full RCE usually needs a second condition such as write access to a Python import path, startup path, or another executable location. That keeps this in HIGH, not CRITICAL.

"Serious bug, but not an internet-wide fire drill: arbitrary file write is real, broad pre-auth RCE is not"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach an exposed OpenChatBI chat or API surface

The attacker first needs access to an OpenChatBI deployment that exposes the agent or UI path capable of invoking tools. OpenChatBI ships as a self-hosted Python package with sample Streamlit/Gradio interfaces, so the practical attack surface is whatever the operator exposed, not a giant SaaS fleet.
Conditions required:
  • A vulnerable OpenChatBI instance <=0.2.1 is deployed
  • The attacker can reach the chat/API surface that can trigger save_report
  • The deployment is not isolated behind VPN, SSO, IP allowlisting, or internal-only routing
Where this breaks in practice:
  • OpenChatBI is a niche OSS package, not a default enterprise platform
  • Many BI assistants are only reachable on internal networks
  • There is no reliable internet census fingerprint showing broad exposure
Detection/coverage: External scanners can version-match the package or image, but pure network fingerprinting is weak because OpenChatBI commonly sits behind generic FastAPI/Streamlit/Gradio front ends.
STEP 02

Coerce the LLM to call save_report with traversal input

The exploit path is not a raw HTTP parameter smash against a classic file-download endpoint; it abuses an LLM tool invocation. Public issue #10 describes manipulating the model to call save_report with a malicious file_format, effectively using the agent as the delivery vehicle.
Conditions required:
  • The deployment exposes save_report to the agent workflow
  • The model accepts attacker-controlled prompts or content
  • There is no prompt-layer validation or server-side schema enforcement beyond the vulnerable code
Where this breaks in practice:
  • Some deployments may not expose the reporting tool in all workflows
  • Application owners may already constrain tools by role or workflow
  • Prompt attack reliability varies by model behavior and guardrails
Detection/coverage: Best coverage is application logging of tool calls and arguments; watch for ../, /../, or \..\ inside file_format.
STEP 03

Abuse path traversal for arbitrary file write

In vulnerable code, file_format.lstrip(".") removes only leading dots and does not block separators or traversal sequences. The generated filename preserves malicious path segments, allowing writes outside the configured report directory.
Conditions required:
  • The OpenChatBI process has filesystem write permissions to the target path
  • The underlying OS path semantics allow the traversal string to resolve as intended
Where this breaks in practice:
  • Least-privilege service accounts reduce writable targets
  • Containers with read-only roots or narrow writable volumes sharply cut blast radius
  • SELinux/AppArmor/FIM can expose or block unusual writes
Detection/coverage: Host-based telemetry is strong here: look for the OpenChatBI Python process writing outside its expected report/output directory.
STEP 04

Upgrade file write into code execution or persistence

The public report describes overwriting Python files such as __init__.py, or on Windows, writing to startup locations or PowerShell profile paths. That is plausible, but it is not automatic: the target path must be writable and later executed or imported.
Conditions required:
  • A writable import path, startup folder, cron path, profile path, or equivalent execution sink exists
  • The file is later loaded by the application, Python runtime, or operating system
Where this breaks in practice:
  • Many production containers do not permit writes to application code paths
  • A restart, import event, or operator action may be required before payload execution
  • Wrong target selection degrades impact to simple file corruption or defacement
Detection/coverage: EDR/FIM should catch unexpected changes to app modules, startup folders, profiles, or service-owned directories. This is where mature defenders usually see it.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed active exploitation found in reviewed sources. RAXE explicitly noted *no known exploitation in the wild* as of 2026-03-10.
KEV statusNot KEV-listed in the reviewed material. Treat that as a meaningful downgrade signal versus true internet fire drills.
EPSS0.00089 on 2026-05-30, roughly the 25.35th percentile per FIRST. That's low, and it matches the limited exposure story.
PoC availabilityThere is public reproduction detail in GitHub issue #10, including a described LLM-manipulation path and example impact. That is enough for copycat work even without a polished exploit repo.
Affected versionsopenchatbi <= 0.2.1 per GHSA and NVD.
Fixed versionUpgrade to 0.2.2. The patch commit 372a7e8 replaced lstrip(".") handling with an explicit allowlist: md, csv, txt, json, html, xml.
CVSS vector reality checkVendor/NVD-style AV:N/AC:L/PR:N/UI:N describes the code path in isolation, but it ignores deployment friction: reachable population is narrow, and reliable RCE usually needs a writable execution sink after the arbitrary file write.
Exposure populationOpenChatBI is a niche OSS package with about 566 GitHub stars in the reviewed repo snapshot and sample self-hosted UIs, not a mass-market enterprise appliance. That lowers the expected externally exposed population.
Scanner / census coverageVersion-based package or image scanning should work. Internet census visibility is weak because OpenChatBI often presents through generic Python web stacks; no trustworthy Shodan/Censys count was found.
Disclosure / reporterPublic GitHub advisory published 2026-03-02; NVD published the CVE on 2026-03-06. The public issue credits the bug report to @guokailiang0925.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to HIGH (8.1/10)

The decisive downgrade factor is deployment friction: this is a dangerous arbitrary file write in a niche self-hosted BI assistant, not a proven internet-wide pre-auth RCE on a ubiquitous edge product. The exploit chain gets materially harder in real environments because many deployments are internal and full code execution usually requires a second writable execution sink after the initial file write.

HIGH Technical root cause and affected version range
MEDIUM Real-world exploitation likelihood across enterprise deployments
MEDIUM Blast-radius assessment after friction audit

Why this verdict

  • Start at 9.8, then cut for population. The vendor/NVD baseline assumes a generic unauthenticated network service, but OpenChatBI is a niche self-hosted package with a much smaller exposed install base than mainstream enterprise middleware.
  • Cut again for attacker position. In many real deployments this requires access to an internal BI assistant or developer-run demo surface, which implies a narrower reachable population than edge-facing software.
  • Keep it HIGH because the primitive is still ugly. Arbitrary file write from a remote prompt/tool path is serious, and where the service account can touch executable paths the impact can absolutely become RCE or persistence.

Why not higher?

There is no KEV entry, no credible in-the-wild exploitation evidence, and EPSS is low. More importantly, the chain from file write to full system compromise is conditional: writable execution paths, permissive filesystem rights, and sometimes a restart or import event are all real friction points that stop this from being a straight CRITICAL in most enterprises.

Why not lower?

This is still a remote arbitrary file write on the application host, not a cosmetic bug. Even if code execution does not materialize, unauthorized file creation or overwrite on an analytics server can damage integrity, create persistence, or poison downstream workflows, so MEDIUM would be too forgiving.

05 · Compensating Control

What to do — in priority order.

  1. Constrain filesystem writes — Run the OpenChatBI service account with write access only to the intended report directory and deploy this within the noisgate mitigation SLA for a HIGH finding, meaning within 30 days. This is the single best compensating control because it converts a broad arbitrary file write into a contained report-write failure.
  2. Make the application root read-only — If containerized, use a read-only root filesystem and mount only a narrow writable volume for report output; if not containerized, apply equivalent OS ACLs within 30 days. This directly breaks the most dangerous post-write step: overwriting importable code or startup paths.
  3. Restrict reachability — Put OpenChatBI behind VPN, SSO, reverse-proxy auth, or IP allowlisting within 30 days if it is currently reachable from untrusted networks. The biggest severity reducer in practice is shrinking who can even reach the tool-calling surface.
  4. Log and alert on tool arguments — Capture save_report invocations and alert on file_format values containing /, \, .., or unsupported extensions within 30 days. This gives you early delivery-stage visibility before the arbitrary write lands.
  5. Enable FIM on code and startup paths — Deploy file integrity monitoring for the OpenChatBI package tree, startup folders, profiles, and report directories within 30 days. That will catch the step that matters operationally: unexpected writes outside the normal export path.
What doesn't work
  • A generic perimeter WAF doesn't reliably solve this, because the exploit can arrive as model prompt content and become malicious only when the agent calls save_report.
  • Relying on anti-malware alone is weak; many successful abuse cases are just text file overwrites until a later import or startup event triggers execution.
  • Blocking . characters is not enough; the vulnerable logic already stripped leading dots and still allowed traversal through path separators.
06 · Verification

Crowdsourced verification payload.

Run this on the target host, container, golden image, or CI job that builds the OpenChatBI environment. Invoke it with python3 verify_openchatbi_cve_2026_28795.py or point it at a source tree with python3 verify_openchatbi_cve_2026_28795.py /opt/openchatbi; no admin rights are required, but you need read access to the Python environment or source files.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# verify_openchatbi_cve_2026_28795.py
# Detects whether OpenChatBI is vulnerable to CVE-2026-28795.
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

import os
import re
import sys
from pathlib import Path

try:
    from importlib import metadata as importlib_metadata
except Exception:
    import importlib_metadata  # type: ignore


def parse_version(v):
    parts = re.findall(r"\d+", v or "")
    if not parts:
        return tuple()
    return tuple(int(x) for x in parts[:4])


def version_leq(a, b):
    la = list(a)
    lb = list(b)
    while len(la) < len(lb):
        la.append(0)
    while len(lb) < len(la):
        lb.append(0)
    return tuple(la) <= tuple(lb)


def inspect_file(path: Path):
    try:
        text = path.read_text(encoding="utf-8", errors="ignore")
    except Exception:
        return None

    has_lstrip = 'file_format.lstrip(".")' in text or "file_format.lstrip('.')" in text
    has_allowlist = "allowed_formats" in text and "Unsupported file format" in text

    if has_allowlist and not has_lstrip:
        return "PATCHED"
    if has_lstrip and not has_allowlist:
        return "VULNERABLE"
    if has_lstrip and has_allowlist:
        return "UNKNOWN"
    return None


def candidate_paths(root: Path):
    paths = [
        root / "openchatbi" / "tool" / "save_report.py",
        root / "tool" / "save_report.py",
        root / "save_report.py",
    ]
    return [p for p in paths if p.exists()]


def main():
    target = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else None

    # 1) Source tree inspection if a path argument is provided
    if target:
        if target.is_file():
            result = inspect_file(target)
            if result == "PATCHED":
                print("PATCHED")
                sys.exit(0)
            if result == "VULNERABLE":
                print("VULNERABLE")
                sys.exit(1)
        elif target.is_dir():
            for p in candidate_paths(target):
                result = inspect_file(p)
                if result == "PATCHED":
                    print("PATCHED")
                    sys.exit(0)
                if result == "VULNERABLE":
                    print("VULNERABLE")
                    sys.exit(1)

    # 2) Installed package version check
    try:
        version = importlib_metadata.version("openchatbi")
        pv = parse_version(version)
        if pv:
            if version_leq(pv, (0, 2, 1)):
                print("VULNERABLE")
                sys.exit(1)
            if pv >= (0, 2, 2):
                print("PATCHED")
                sys.exit(0)
    except Exception:
        pass

    # 3) Import path code inspection as fallback
    try:
        import openchatbi  # type: ignore
        pkg_root = Path(openchatbi.__file__).resolve().parent
        for p in candidate_paths(pkg_root.parent):
            result = inspect_file(p)
            if result == "PATCHED":
                print("PATCHED")
                sys.exit(0)
            if result == "VULNERABLE":
                print("VULNERABLE")
                sys.exit(1)
    except Exception:
        pass

    print("UNKNOWN")
    sys.exit(2)


if __name__ == "__main__":
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: find every OpenChatBI deployment and package instance, especially anything reachable from shared user networks or the internet, then either restrict write permissions and exposure within 30 days per the noisgate mitigation SLA or take the service internal immediately if you cannot. For the actual code fix, move all vulnerable <=0.2.1 installs to 0.2.2 within 180 days per the noisgate remediation SLA; there is no immediate-hours override here because there is no KEV listing or credible active exploitation evidence in the reviewed sources.

Sources

  1. NVD CVE-2026-28795
  2. GitHub Security Advisory GHSA-vmwq-8g8c-jm79
  3. Fix commit 372a7e8
  4. Public vulnerability report issue #10
  5. Patch pull request #12
  6. OpenChatBI repository README
  7. PyPI openchatbi release history
  8. FIRST EPSS API for CVE-2026-28795
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.