CVE-2026-40576 is a critical-severity (CVSS 9.4) Path Traversal vulnerability in excel-mcp-server. A fix is available for excel-mcp-server — see the affected versions and patch details below.
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in excel-mcp-server
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-40576.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2026-40576 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 377,333 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
excel-mcp-serverReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
A path traversal vulnerability exists in excel-mcp-server versions up to and including 0.1.7. When running in SSE or Streamable-HTTP transport mode (the documented way to use this server remotely), an unauthenticated attacker on the network can read, write, and overwrite arbitrary files on the host filesystem by supplying crafted filepath arguments to any of the 25 exposed MCP tool handlers.
The server is intended to confine file operations to a directory set by the EXCEL_FILES_PATH environment variable. The function responsible for enforcing this boundary — get_excel_path() — fails to do so due to two independent flaws: it passes absolute paths through without any check, and it joins relative paths without resolving or validating the result. Combined with zero authentication on the default network-facing transport and a default bind address of 0.0.0.0 (all interfaces), this allows trivial remote exploitation.
Details
| Field | Value |
|---|---|
| Package | excel-mcp-server (PyPI) |
| Repository | https://github.com/haris-musa/excel-mcp-server |
| Affected versions | <= 0.1.7 |
| Tested version | 0.1.7 — commit de4dc75 |
| CWE | CWE-22 — Improper Limitation of a Pathname to a Restricted Directory |
| CVSS 3.1 | 8.2 High — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H |
| Transports affected | sse, streamable-http (network-facing) |
| Authentication required | None |
Vulnerable Code
The root cause is in src/excel_mcp/server.py, lines 75–94:
def get_excel_path(filename: str) -> str:
"""Get full path to Excel file."""
# FLAW 1 — absolute paths bypass the sandbox entirely
if os.path.isabs(filename):
return filename # line 86: returned as-is
# In SSE/HTTP mode, EXCEL_FILES_PATH is set
if EXCEL_FILES_PATH is None:
raise ValueError(...)
# FLAW 2 — relative paths joined without boundary validation
return os.path.join(EXCEL_FILES_PATH, filename) # line 94: "../" escapes
Why this is exploitable
Flaw 1 — Absolute path bypass (line 86):
If the attacker passes filepath="/etc/shadow" or filepath="/home/user/secrets.xlsx", the function returns it unchanged. The sandbox directory EXCEL_FILES_PATH is never consulted.
Flaw 2 — Relative traversal (line 94):
os.path.join("/srv/sandbox", "../../etc/passwd") produces "/srv/sandbox/../../etc/passwd", which resolves to "/etc/passwd". No os.path.realpath() or os.path.commonpath() check is performed, so ../ sequences escape the sandbox.
Contributing factors that increase severity
-
Default bind address is
0.0.0.0(all interfaces) —server.pyline 70:host=os.environ.get("FASTMCP_HOST", "0.0.0.0"),A user who follows the README and runs
uvx excel-mcp-server streamable-httpwithout explicitly settingFASTMCP_HOSTexposes the server to their entire LAN. -
Zero authentication — FastMCP's SSE and Streamable-HTTP transports ship with no authentication. The server adds none. Any TCP client that reaches port 8017 can call any tool.
-
All 25 tool handlers are affected — every
@mcp.tool()decorated function callsget_excel_path(filepath)as its first action. This is not an isolated endpoint; it is the entire API surface. -
Arbitrary directory creation —
src/excel_mcp/workbook.pyline 24 runspath.parent.mkdir(parents=True, exist_ok=True)before saving, meaning the attacker can create directory trees at any writable location.
Proof of Concept
Video demonstration
asciinema recording: https://asciinema.org/a/2HVA3uKvVeFahIXY
I have also attached the full PoC shell script (record-poc.sh) and the Python exploit script (exploit_test.py) to a Google Drive for the maintainer to review and reproduce independently:
Google Drive (PoC scripts): Shared privately via email
Contents:
record-poc.sh— automated PoC recording script (bash)exploit_test.py— Python exploit that tests all 7 primitives against a running server
Setup
# install
pip install excel-mcp-server mcp httpx
# start server with a sandbox directory
mkdir -p /tmp/sandbox
EXCEL_FILES_PATH=/tmp/sandbox FASTMCP_HOST=127.0.0.1 FASTMCP_PORT=8017 \
excel-mcp-server streamable-http
Exploit script
The following Python script connects to the server with zero credentials and demonstrates all exploitation primitives:
{REMOVING CAUSE FOR SAFETY CONCERNS}
Results
All 7 test cases passed against a live server instance:
CONFIRMED: 7 | FAILED: 0
[CONFIRMED] AUTH: Connected with ZERO authentication. 25 tools exposed.
[CONFIRMED] P1-WRITE-ABS: file exists=True size=4783B (outside sandbox)
[CONFIRMED] P2-WRITE-TRAVERSAL: escaped sandbox via ../ exists=True
[CONFIRMED] P3-MKDIR: attacker directory tree created=True
[CONFIRMED] P4-READ: exfiltrated SSN=True name=True
[CONFIRMED] P5-OVERWRITE: victim data replaced=True
[CONFIRMED] P6-STAT: server attempted to open /etc/hostname (format error confirms file access)
Filesystem evidence (independently verified after exploit)
Files created outside the sandbox:
$ ls -la /tmp/cve-hunt/outside_sandbox/
-rw-rw-r-- 1 hitarth hitarth 4783 Apr 10 17:36 P1_absolute_write.xlsx
-rw-rw-r-- 1 hitarth hitarth 4783 Apr 10 17:36 P2_traversal_write.xlsx
Attacker-created directory tree:
$ find /tmp/cve-hunt/attacker_dir
/tmp/cve-hunt/attacker_dir
/tmp/cve-hunt/attacker_dir/deep
/tmp/cve-hunt/attacker_dir/deep/nested
/tmp/cve-hunt/attacker_dir/deep/nested/x.xlsx
Victim file after overwrite (original SSN destroyed):
('SSN', 'Name')
('ATTACKER-CONTROLLED', 'PWNED') # was: ('123-45-6789', 'Alice Johnson')
('987-65-4321', 'Bob Smith')
Impact
An unauthenticated network attacker can:
| What | How | Severity |
|---|---|---|
Read any .xlsx file on the host | Supply absolute path to read_data_from_excel | Confidentiality loss — cross-tenant data theft of financial data, HR records, reports |
Write .xlsx files anywhere on the filesystem | Supply absolute path or ../ traversal to create_workbook or write_data_to_excel | Integrity loss — destroy or corrupt any writable .xlsx, plant malicious files |
| Create arbitrary directories anywhere writable | create_workbook triggers mkdir(parents=True) on attacker-controlled path | Precursor to privilege escalation or DoS |
| Overwrite existing business files with attacker content | write_data_to_excel with absolute path to target | Silent data corruption — audit reports, salary sheets, financial models |
| Fill disk via repeated writes | Loop create_workbook with unique filenames | Denial of service — crash services dependent on free disk |
Plant macro-enabled templates (.xltm) at known shared paths | create_workbook at path like /home/user/Templates/report.xltm | Client-side RCE chain when downstream user opens the template in Excel |
Who is affected
Anyone running excel-mcp-server in SSE or Streamable-HTTP mode on a reachable network — which is the documented and recommended deployment for remote use. The project README explicitly states:
- "Works both locally and as a remote service"
- "Streamable HTTP Transport (Recommended for remote connections)"
The server has 3,655+ GitHub stars and is published on PyPI with active downloads.
Suggested Fix
Replace get_excel_path() with a version that enforces the sandbox boundary:
import os
def get_excel_path(filename: str) -> str:
if EXCEL_FILES_PATH is None:
# stdio mode: local caller is trusted
if not os.path.isabs(filename):
raise ValueError("must be absolute path in stdio mode")
return filename
# Remote mode (SSE / streamable-http): enforce sandbox
if os.path.isabs(filename):
raise ValueError("absolute paths are not permitted in remote mode")
if "\x00" in filename:
raise ValueError("NUL byte in filename")
base = os.path.realpath(EXCEL_FILES_PATH)
candidate = os.path.realpath(os.path.join(base, filename))
if not candidate.startswith(base + os.sep) and candidate != base:
raise ValueError(f"path escapes EXCEL_FILES_PATH: {filename}")
return candidate
References
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | excel-mcp-server | all versions | 0.1.8pip install --upgrade 'excel-mcp-server==0.1.8' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for excel-mcp-server, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update excel-mcp-server to 0.1.8 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-40576 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-40576 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-40576. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2026-40576 in your dependencies?
O3 Security finds CVE-2026-40576 across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.