# REPRO-2026-00334: Authenticated command injection in pm2panel's /restart handler allows remote shell command execution on the host. ## Summary Status: published Severity: high CVSS: Unknown CWE: CWE-78 (OS Command Injection) (Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')) Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00334 CVE: CVE-2026-72573 ## Package Name: 4xmen/pm2panel Ecosystem: GitHub / Node.js web application Affected: All versions (version 0 affected per CVE record, defaultStatus unknown) Fixed: None identified - no fix available ## Root Cause # CVE-2026-72573: Command Injection in pm2panel /restart Endpoint ## Summary The `4xmen/pm2panel` web application contains an OS command injection vulnerability in the authenticated `/restart` HTTP endpoint. The handler at line 188 of `pm2panel.js` concatenates the attacker-controlled `req.query.id` query parameter directly into a `child_process.exec()` shell command (`exec("pm2 restart " + req.query.id)`). Because `exec` invokes `/bin/sh`, shell metacharacters such as `;`, `&&`, `|`, backticks, or `$()` in the `id` parameter are interpreted by the shell, allowing an authenticated attacker to execute arbitrary commands on the host with the privileges of the pm2panel process. ## Impact - **Package/component affected:** `4xmen/pm2panel` (Node.js web application, `pm2panel.js`) - **Affected versions:** All versions (commit `dd2a7d2e8cb4dacefb618ab54b0a8f7dc6742fa0`, latest on `master`) - **Risk level:** High — authenticated remote code execution - **Consequences:** An attacker with valid panel credentials (default: `admin`/`admin`) can execute arbitrary shell commands on the host server, leading to full system compromise, data exfiltration, lateral movement, or service disruption. ## Impact Parity - **Disclosed/claimed maximum impact:** Code execution (CWE-78, OS command injection) - **Reproduced impact from this run:** Full arbitrary command execution — a marker file was created on the host filesystem via an injected `touch` command through the authenticated `/restart` endpoint - **Parity:** `full` - **Not demonstrated:** N/A — the full claimed impact was demonstrated ## Root Cause The `/restart` route handler in `pm2panel.js` (line 188) constructs a shell command by directly string-concatenating the user-supplied `req.query.id` query parameter: ```js app.get('/restart', function (req, res) { if (!req.session.islogin) { // redirect to login... } else { if (req.query.id) { exec("pm2 restart " + req.query.id, (error, stdout, stderr) => { // ... }); } } }); ``` `child_process.exec()` spawns a shell (`/bin/sh -c`) to run the command. The `req.query.id` value is never validated, sanitized, or shell-escaped. When an attacker sends a request like `GET /restart?id=0; touch /tmp/pm2panel_pwned`, the shell interprets the `;` as a command separator and executes `touch /tmp/pm2panel_pwned` in addition to `pm2 restart 0`. The same vulnerable pattern exists in four other handlers: - `/start` (line 218): `exec("pm2 start " + req.query.id)` - `/stop` (line 248): `exec("pm2 stop " + req.query.id)` - `/delete` (line 278): `exec("pm2 delete " + req.query.id)` - `/addProccess` (line 149): `exec('pm2 start "' + req.body.path + '"')` No fix commit has been identified; the vulnerability is present in the latest commit on `master`. ## Reproduction Steps 1. **Reference script:** `bundle/repro/reproduction_steps.sh` 2. **What the script does:** - Clones/reuses the `4xmen/pm2panel` repository from the project cache - Installs system dependencies (`libpam0g-dev`) and npm dependencies (including native `node-linux-pam` module) - Installs and starts PM2 with a demo process (id 0) - Starts the pm2panel Express web application on port 3001 - Authenticates via `POST /loginCheck` with default credentials (`admin`/`admin`) - Sends an authenticated `GET /restart?id=0; touch /tmp/pm2panel_pwned_` request - Verifies the marker file was created (proving arbitrary command execution) - Runs negative controls: unauthenticated request is rejected (302 redirect), safe request without injection does not create a marker 3. **Expected evidence of reproduction:** - Marker file exists at `/tmp/pm2panel_pwned_*` after the exploit request - HTTP 302 response from the exploit endpoint (normal redirect behavior) - Unauthenticated requests return 302 redirect to `/login` - Safe restart requests (no injection) do not create marker files ## Evidence - **Log files:** - `bundle/logs/reproduction_steps.log` — full script execution log - `bundle/logs/pm2panel_service.log` — pm2panel application server log - `bundle/logs/artifacts/http/response_login.txt` — login response with session cookie - `bundle/logs/artifacts/http/request_exploit.txt` — exploit request details - `bundle/logs/artifacts/http/response_exploit.txt` — exploit HTTP response (302) - `bundle/logs/artifacts/http/marker_evidence.txt` — marker file existence and stat output - `bundle/logs/artifacts/http/response_unauth.txt` — unauthenticated request response (302 redirect) - `bundle/logs/artifacts/http/safe_restart_status.txt` — safe restart response code - `bundle/repro/runtime_manifest.json` — structured runtime evidence manifest - **Key excerpts:** - Exploit request: `GET /restart?id=0;%20touch%20/tmp/pm2panel_pwned_3850 HTTP/1.1` - Exploit response: `HTTP/1.1 302 Found` with `Location: /` - Marker evidence: `MARKER_FILE_EXISTS=true` with `stat` output showing file creation timestamp - Negative control (unauthenticated): `302` redirect to `/login` - Negative control (safe): no marker file created - **Environment:** - Node.js v24.18.0, npm 11.16.0 - PM2 v7.0.3 - pm2panel commit `dd2a7d2e8cb4dacefb618ab54b0a8f7dc6742fa0` - Linux x86_64, Express 4.x, express-session ## Recommendations / Next Steps 1. **Fix:** Replace `child_process.exec` with `child_process.execFile` (which does not invoke a shell) and pass `req.query.id` as a separate argument array, or validate `req.query.id` against a strict numeric regex before use. 2. **Defense in depth:** Implement input validation on all endpoints that accept process IDs (`/start`, `/stop`, `/delete`, `/addProccess`). 3. **Authentication:** Change default credentials from `admin`/`admin` and enforce strong password policies. 4. **Upgrade guidance:** No patched version exists. Users should apply the fix manually or discontinue use of the panel. 5. **Testing:** Add integration tests that send shell metacharacters in query parameters and assert they are not interpreted by the shell. ## Additional Notes - **Idempotency:** The script uses process-specific marker file names (with `$$` PID suffix) and cleans up PM2 processes and the pm2panel server on each run. It was verified to pass on two consecutive executions. - **Authentication requirement:** The vulnerability requires authentication. The script performs a proper login flow with `POST /loginCheck` and session cookie extraction before sending the exploit request. - **Multiple vulnerable endpoints:** The same command injection pattern affects `/start`, `/stop`, `/delete`, and `/addProccess` in addition to `/restart`. The reproduction focuses on `/restart` as specified in the claim. ## Reproduction Details Reproduced: 2026-08-23T15:44:16.384Z Duration: 854 seconds Tool calls: 174 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00334 pruva-verify CVE-2026-72573 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00334&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00334/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-72573 - Source: https://nvd.nist.gov/vuln/detail/CVE-2026-72573 ## Artifacts - bundle/repro/rca_report.md (analysis, 6794 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 13715 bytes) - bundle/logs/artifacts/http/negative_control.txt (other, 282 bytes) - bundle/logs/artifacts/http/pm2panel_service_final.log (log, 455 bytes) - bundle/logs/artifacts/http/response_login.txt (other, 8887 bytes) - bundle/logs/artifacts/http/safe_restart_status.txt (other, 3 bytes) - bundle/repro/runtime_manifest.json (other, 1214 bytes) - bundle/repro/validation_verdict.json (other, 859 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00334 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00334/artifacts/bundle/repro/reproduction_steps.sh - Web: https://www.pruva.dev/reproductions/REPRO-2026-00334 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://www.pruva.dev