Vulnerability Summary
Type: Path Traversal via Symlink Escape
Severity: High (CVSS 3.1: 8.1)
File: src/mcp-server/state.ts
Function: resolvePath()
Lines: ~128-145
Description
The resolvePath() function uses path.normalize() with string prefix matching to enforce the FS_BASE_DIRECTORY boundary. However, it does NOT resolve symbolic links before performing this check.
An attacker who can write files within the allowed directory can create symbolic links pointing to files outside the boundary (e.g., /etc/passwd, /root/.ssh/*, /proc/*/environ), then use the MCP tool to read them through the symlink, bypassing the security check entirely.
Proof of Concept
- Set
FS_BASE_DIRECTORY=/allowed/dir
- Create a symlink inside the allowed directory:
ln -s /etc/passwd /allowed/dir/evil_link
- Call the MCP tool with path
evil_link
- The server reads
/etc/passwd contents, bypassing the boundary check
Root Cause
// Current code - vulnerable
if (this.fsBaseDirectory) {
const normalizedFsBaseDirectory = path.normalize(this.fsBaseDirectory);
const normalizedSanitizedAbsolutePath = path.normalize(sanitizedAbsolutePath);
// String prefix check - NO symlink resolution!
if (!normalizedSanitizedAbsolutePath.startsWith(normalizedFsBaseDirectory + path.sep)
&& normalizedSanitizedAbsolutePath !== normalizedFsBaseDirectory) {
throw new McpError(...);
}
}
path.normalize() and string prefix matching do not resolve symbolic links. The fs.readFile() call follows the symlink at read time, bypassing the boundary check.
Impact
- Read arbitrary files on the server (passwd, SSH keys, credentials, environment variables)
- Information disclosure enabling further attacks
- Potential secret/key leakage via
/proc or .ssh directories
Recommended Fix
Resolve symlinks before the boundary check:
// Fixed code - safe
const realPath = fs.realpathSync(sanitizedAbsolutePath);
const normalizedRealPath = path.normalize(realPath);
if (!normalizedRealPath.startsWith(normalizedFsBaseDirectory + path.sep)
&& normalizedRealPath !== normalizedFsBaseDirectory) {
throw new McpError(...);
}
Timeline
- Discovery Date: 2026-05-06
- Report Date: 2026-05-06
This report is submitted in good faith with the goal of improving security. I request coordinated disclosure and will credit the finding as specified by your project security policy.
Best regards,
Security Researcher
Vulnerability Summary
Type: Path Traversal via Symlink Escape
Severity: High (CVSS 3.1: 8.1)
File: src/mcp-server/state.ts
Function: resolvePath()
Lines: ~128-145
Description
The
resolvePath()function usespath.normalize()with string prefix matching to enforce theFS_BASE_DIRECTORYboundary. However, it does NOT resolve symbolic links before performing this check.An attacker who can write files within the allowed directory can create symbolic links pointing to files outside the boundary (e.g.,
/etc/passwd,/root/.ssh/*,/proc/*/environ), then use the MCP tool to read them through the symlink, bypassing the security check entirely.Proof of Concept
FS_BASE_DIRECTORY=/allowed/direvil_link/etc/passwdcontents, bypassing the boundary checkRoot Cause
path.normalize()and string prefix matching do not resolve symbolic links. Thefs.readFile()call follows the symlink at read time, bypassing the boundary check.Impact
/procor.sshdirectoriesRecommended Fix
Resolve symlinks before the boundary check:
Timeline
This report is submitted in good faith with the goal of improving security. I request coordinated disclosure and will credit the finding as specified by your project security policy.
Best regards,
Security Researcher