CVE-2026-29787
mcp-memory-service exposes system info via unauthenticated health endpoint.
- CVSS 5.3
- CWE-200
- Information Leakage
- Remote
mcp-memory-service is an open-source memory backend for multi-agent systems. Prior to version 10.21.0, the /api/health/detailed endpoint returns detailed system information including OS version, Python version, CPU count, memory totals, disk usage, and the full database filesystem path. When MCP_ALLOW_ANONYMOUS_ACCESS=true is set (required for the HTTP server to function without OAuth/API key), this endpoint is accessible without authentication. Combined with the default 0.0.0.0 binding, this exposes sensitive reconnaissance data to the entire network. This issue has been patched in version 10.21.0.
- CWE
- CWE-200
- CVSS base score
- 5.3
- Published
- 2026-03-07
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Information Leakage
- Subcategory
- Information Disclosure
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- mcp-memory-s
- Fixed by upgrading
- Yes
Solution
Upgrade mcp-memory-service to version 10.21.0 or later.
Vulnerable code sample
import os
import sys
import platform
import psutil
from flask import Flask, jsonify
# --- Vulnerable Code Simulation ---
# This code represents the state of the application BEFORE the patch for CVE-2026-29787.
# The vulnerability lies in the unauthenticated exposure of the /api/health/detailed endpoint
# when the application is configured to allow anonymous access.
# Assume MCP_ALLOW_ANONYMOUS_ACCESS is set to "true" via an environment variable.
# For this demonstration, we'll proceed as if this condition is met.
app = Flask(__name__)
# This would typically be loaded from a configuration file or environment.
# Exposing this is a key part of the vulnerability.
DATABASE_FILESYSTEM_PATH = "/var/lib/mcp/data/memory_database.db"
@app.route('/api/health/detailed', methods=['GET'])
def get_detailed_health():
"""
VULNERABLE ENDPOINT: Exposes detailed system and application information
without any authentication or authorization checks.
"""
try:
# Gather sensitive system information
os_version = platform.platform()
python_version = sys.version
cpu_count = os.cpu_count()
# Memory usage
memory = psutil.virtual_memory()
memory_total_gb = round(memory.total / (1024 ** 3), 2)
memory_used_gb = round(memory.used / (1024 ** 3), 2)
memory_percent = memory.percent
# Disk usage for the root partition
disk = psutil.disk_usage('/')
disk_total_gb = round(disk.total / (1024 ** 3), 2)
disk_used_gb = round(disk.used / (1024 ** 3), 2)
disk_percent = disk.percent
# Construct the detailed response payload
detailed_info = {
"status": "ok",
"system_details": {
"os_version": os_version,
"python_version": python_version,
"cpu_count": cpu_count,
"memory": {
"total_gb": memory_total_gb,
"used_gb": memory_used_gb,
"usage_percent": memory_percent
},
"disk": {
"total_gb": disk_total_gb,
"used_gb": disk_used_gb,
"usage_percent": disk_percent
}
},
"database": {
"path": DATABASE_FILESYSTEM_PATH # Leaking the full filesystem path
}
}
return jsonify(detailed_info), 200
except Exception as e:
return jsonify({"status": "error", "message": str(e)}), 500
if __name__ == '__main__':
# The default binding to '0.0.0.0' makes the server accessible from the entire network.
# Combined with the lack of authentication on the detailed health endpoint, this
# exposes sensitive reconnaissance data.
print("Starting vulnerable mcp-memory-service...")
print("WARNING: MCP_ALLOW_ANONYMOUS_ACCESS is enabled.")
print("WARNING: The /api/health/detailed endpoint is exposed without authentication.")
app.run(host='0.0.0.0', port=8000)Patched code sample
import os
import sys
import psutil
import shutil
from typing import Optional
from fastapi import FastAPI, Depends, Header, HTTPException, status
from pydantic import BaseModel, Field
# --- Configuration ---
# In a real application, this would be loaded from environment variables.
ADMIN_API_KEY = "fixed-secret-api-key-required-for-details"
DB_FILESYSTEM_PATH = "/var/data/mcp/memory.db"
# This represents the vulnerable setting. Even if true, the fix ensures the
# sensitive endpoint remains protected.
MCP_ALLOW_ANONYMOUS_ACCESS = True
# --- FastAPI Application Setup ---
# This code represents the patched version (e.g., 10.21.0 or later).
app = FastAPI(
title="MCP Memory Service (Patched)",
version="10.21.0",
)
# --- Pydantic Models for Response Payloads ---
class BasicHealthResponse(BaseModel):
status: str = "ok"
class DetailedHealthResponse(BaseModel):
os_version: str
python_version: str
cpu_count: int
memory_total_gb: float
disk_total_gb: float
disk_free_gb: float
database_path: str
# --- Authentication Dependency (THE FIX) ---
# This dependency function is the core of the fix. It is applied to any
# endpoint that must be protected, ensuring an authorization check occurs
# before the endpoint's logic is executed.
async def require_admin_authentication(x_admin_api_key: Optional[str] = Header(None)):
"""
Checks for a valid admin API key in the request headers.
If the key is missing or incorrect, it raises an HTTP 401 Unauthorized error.
"""
if not x_admin_api_key or x_admin_api_key != ADMIN_API_KEY:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="A valid X-Admin-API-Key header is required for this endpoint",
)
# --- API Endpoints ---
@app.get(
"/api/health",
tags=["Health"],
summary="Public Basic Health Check",
response_model=BasicHealthResponse
)
async def get_basic_health():
"""
A basic, non-sensitive health check that can remain public.
This endpoint does not have the authentication dependency.
"""
return {"status": "ok"}
@app.get(
"/api/health/detailed",
tags=["Health"],
summary="Protected Detailed System Health Information",
response_model=DetailedHealthResponse,
# VULNERABILITY FIX: The `dependencies` list now includes the
# `require_admin_authentication` check. This prevents unauthenticated
# access to the sensitive information returned by this endpoint.
dependencies=[Depends(require_admin_authentication)],
)
async def get_detailed_health():
"""
Returns detailed and sensitive system information. Access is now restricted
to authenticated users only, resolving the information disclosure vulnerability.
"""
mem = psutil.virtual_memory()
disk = shutil.disk_usage("/")
try:
# os.uname() is not available on Windows
os_info = f"{sys.platform} {os.uname().release}"
except AttributeError:
os_info = f"{sys.platform} {sys.getwindowsversion().major}.{sys.getwindowsversion().minor}"
return DetailedHealthResponse(
os_version=os_info,
python_version=sys.version,
cpu_count=os.cpu_count() or 0,
memory_total_gb=round(mem.total / (1024 ** 3), 2),
disk_total_gb=round(disk.total / (1024 ** 3), 2),
disk_free_gb=round(disk.free / (1024 ** 3), 2),
database_path=DB_FILESYSTEM_PATH,
)Cite this entry
@misc{vaitp:cve202629787,
title = {{mcp-memory-service exposes system info via unauthenticated health endpoint.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-29787},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-29787/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
