[PR #36] Command Injection Vulnerability in Environment Variable Substitution Script #37

Open
opened 2026-03-03 12:00:26 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/finmars-platform/finmars-vue-portal/pull/36
Author: @Moltivie
Created: 1/21/2026
Status: 🔄 Open

Base: mainHead: main


📝 Commits (1)

  • 5558679 fix command injection vulnerability in environment substitution

📊 Changes

1 file changed (+70 additions, -16 deletions)

View changed files

📝 docker/substitute_environment_variables.sh (+70 -16)

📄 Description

Vulnerability Description

A Remote Code Execution (RCE) vulnerability existed in the Docker environment variable substitution script (docker/substitute_environment_variables.sh). The script used sed commands to replace placeholder values with environment variables in production .mjs files. By injecting malicious content into environment variables, an attacker could execute arbitrary shell commands during container startup.

Root Cause

The vulnerable code concatenated environment variables directly into sed commands without proper escaping:

sed -i 's|==PROD_API_HOST==|'${PROD_API_HOST}'|g' $file

When the shell expands ${PROD_API_HOST}, if the variable contains the delimiter character |, the attacker can terminate the substitution command and inject a new sed command with the /e flag to execute arbitrary code.

Proof of Concept (PoC)

Attack Vector:

Set the environment variable to include a delimiter and malicious sed command:

export PROD_API_HOST='|; s/^/echo "HACKED" > \/tmp\/pwned/e; s/'

What happens:

The resulting sed command becomes:

sed -i 's|==PROD_API_HOST==||; s/^/echo "HACKED" > /tmp/pwned/e; s/|g' file.mjs

Breakdown:

  1. s|==PROD_API_HOST==|| - Replaces placeholder with empty string
  2. ; - Ends the first command
  3. s/^/echo "HACKED" > \/tmp\/pwned/e - The /e flag executes the shell command echo "HACKED" > /tmp/pwned
  4. ; - Additional command separator
  5. The trailing |g gets absorbed as garbage

Result: The command echo "HACKED" > /tmp/pwned is executed on the host system with the container's privileges.

Solution Implemented

Replaced the vulnerable sed-based approach with a secure Node.js script that:

  1. Treats strings as literals - No shell command interpretation
  2. Uses String.split().join() - Safe replacement method without regex metacharacter issues
  3. Handles special characters correctly - Properly processes &, \, |, and other special characters common in URLs
  4. No additional dependencies - Uses the existing Node.js 22 runtime already in the node:22-alpine container
  5. Better performance - Single process instead of forking sed/awk multiple times per file

Implementation

The script now generates a temporary Node.js module that reads environment variables via process.env (already isolated) and performs string replacement using safe methods:

// Safe replacement using split/join
content = content.split(placeholder).join(value);

This approach is immune to command injection because:

  • Variables are accessed as JavaScript strings, not shell-expanded
  • No command delimiters (|, ;, etc.) have special meaning
  • The replacement value is treated as pure data, never as code

Additional Fixes

  • Bug fix: Corrected PROD_FRONT_URLPROD_FRONT_HOST to match the actual placeholder in nuxt.config.ts
  • Consistency: Used ROOT_DIR variable consistently throughout the script

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/finmars-platform/finmars-vue-portal/pull/36 **Author:** [@Moltivie](https://github.com/Moltivie) **Created:** 1/21/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `main` --- ### 📝 Commits (1) - [`5558679`](https://github.com/finmars-platform/finmars-vue-portal/commit/55586790241634a623989cc74c6b8c269856cb65) fix command injection vulnerability in environment substitution ### 📊 Changes **1 file changed** (+70 additions, -16 deletions) <details> <summary>View changed files</summary> 📝 `docker/substitute_environment_variables.sh` (+70 -16) </details> ### 📄 Description ## Vulnerability Description A **Remote Code Execution (RCE)** vulnerability existed in the Docker environment variable substitution script (`docker/substitute_environment_variables.sh`). The script used `sed` commands to replace placeholder values with environment variables in production `.mjs` files. By injecting malicious content into environment variables, an attacker could execute arbitrary shell commands during container startup. ### Root Cause The vulnerable code concatenated environment variables directly into `sed` commands without proper escaping: ```bash sed -i 's|==PROD_API_HOST==|'${PROD_API_HOST}'|g' $file ``` When the shell expands `${PROD_API_HOST}`, if the variable contains the delimiter character `|`, the attacker can terminate the substitution command and inject a new `sed` command with the `/e` flag to execute arbitrary code. ## Proof of Concept (PoC) **Attack Vector:** Set the environment variable to include a delimiter and malicious sed command: ```bash export PROD_API_HOST='|; s/^/echo "HACKED" > \/tmp\/pwned/e; s/' ``` **What happens:** The resulting sed command becomes: ```bash sed -i 's|==PROD_API_HOST==||; s/^/echo "HACKED" > /tmp/pwned/e; s/|g' file.mjs ``` **Breakdown:** 1. `s|==PROD_API_HOST==||` - Replaces placeholder with empty string 2. `;` - Ends the first command 3. `s/^/echo "HACKED" > \/tmp\/pwned/e` - The `/e` flag executes the shell command `echo "HACKED" > /tmp/pwned` 4. `;` - Additional command separator 5. The trailing `|g` gets absorbed as garbage **Result:** The command `echo "HACKED" > /tmp/pwned` is executed on the host system with the container's privileges. ## Solution Implemented Replaced the vulnerable `sed`-based approach with a **secure Node.js script** that: 1. **Treats strings as literals** - No shell command interpretation 2. **Uses `String.split().join()`** - Safe replacement method without regex metacharacter issues 3. **Handles special characters correctly** - Properly processes `&`, `\`, `|`, and other special characters common in URLs 4. **No additional dependencies** - Uses the existing Node.js 22 runtime already in the `node:22-alpine` container 5. **Better performance** - Single process instead of forking sed/awk multiple times per file ### Implementation The script now generates a temporary Node.js module that reads environment variables via `process.env` (already isolated) and performs string replacement using safe methods: ```javascript // Safe replacement using split/join content = content.split(placeholder).join(value); ``` This approach is immune to command injection because: - Variables are accessed as JavaScript strings, not shell-expanded - No command delimiters (`|`, `;`, etc.) have special meaning - The replacement value is treated as pure data, never as code ## Additional Fixes - **Bug fix**: Corrected `PROD_FRONT_URL` → `PROD_FRONT_HOST` to match the actual placeholder in `nuxt.config.ts` - **Consistency**: Used `ROOT_DIR` variable consistently throughout the script --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/finmars-vue-portal#37
No description provided.