Skip to content

Remote Command Injection via Unquoted Environment Variable Keys (SSH / WSL Remote) in Zed v0.225.9

High
swannysec published GHSA-63qj-jc2q-7hg5 May 8, 2026

Package

No package listed

Affected versions

<=0.225.9

Patched versions

>=v0.227.1

Description

Hi Zed team,

Thank you for your work on Zed. I found and verified a command injection issue in the remote terminal command-building path and wanted to share a clear, reproducible report to help with triage and fixing.

I tested this on:

  • Zed version: v0.225.9 (release tag)
  • Client: macOS Zed desktop
  • Remote mode: SSH remote project
  • Remote host: Linux (/bin/bash)

Summary

Zed v0.225.9 builds SSH/WSL remote commands as a shell command string that starts with exec env ..., but environment variable keys are inserted without shell quoting or validation.  

If an attacker can control an environment variable key (for example via project terminal settings), shell expansions in the key (such as $(...)) are evaluated by the remote shell when a terminal is opened. This can lead to arbitrary command execution on the remote host under the victim user's account.

Severity (my estimate): High (CWE-78, OS Command Injection)

Details

Root cause

In the SSH and WSL remote command builders, Zed constructs a shell command string like:

exec env KEY=VALUE program args...

The value is quoted for the target shell, but the key is written directly into the shell command string.

This means a malicious key such as ZED$(touch /tmp/pwned) is parsed by the shell, and the command substitution executes before exec env runs.

Incriminated source code (v0.225.9)

SSH (POSIX path):

WSL:

The relevant logic (simplified) is:

write!(exec, "exec env ")?;
for (k, v) in input_env.iter() {
    write!(exec, "{}={} ", k, shell_kind.try_quote(v)?)?;
}

Reachability from project settings (real user workflow)

Project terminal settings are merged into the terminal environment:

The remote terminal path then passes this env map into build_command(...):

Scope notes

  • Affects SSH remote (POSIX shell path) and WSL command-building paths.
  • I verified the issue through the SSH remote terminal flow.
  • The Windows-over-SSH path in v0.225.9 appears to skip setting env vars in that code path, so this specific bug may not apply there.

PoC

Below are complete reproduction steps on a real Zed instance (no file uploads required).

Reproduction prerequisites

  • Zed v0.225.9
  • Open a remote SSH project in Zed (not a local folder)
  • Remote host uses a POSIX shell (for example /bin/bash)

Step 1: Create project settings in the correct path

Important: the file must be inside .zed/settings.json in the project root.

Example path on the remote host:

/home/<user>/<project>/.zed/settings.json

Create the file with this content:

{
  "terminal": {
    "env": {
      "ZED_POC_NORMAL": "works",
      "ZED$(touch /tmp/zed_remote_env_key_injection)": "1"
    }
  }
}

ZED_POC_NORMAL is included as a simple sanity check to confirm the project terminal settings are being applied.

Step 2: Open a fresh terminal in the same remote project

  1. Close existing terminal tabs in that remote workspace
  2. Open a new integrated terminal in the remote project

The terminal should be newly opened because the environment is prepared during terminal launch.

Step 3: Verify settings were applied and payload executed

Run these commands in the remote terminal:

echo "$ZED_POC_NORMAL"
ls -l /tmp/zed_remote_env_key_injection

Expected result

ZED_POC_NORMAL should prove the project settings env was loaded:

works

The injected file should exist on the remote host, proving command execution:

-rw-r--r-- 1 <user> <group> 0 <date> /tmp/zed_remote_env_key_injection

Optional cleanup:

rm -f /tmp/zed_remote_env_key_injection

Please check my test here:

zed

Impact

What kind of vulnerability is it?

  • OS Command Injection (CWE-78)

Who is impacted?

  • Users opening a remote SSH/WSL project in Zed where attacker-controlled environment keys can flow into the remote terminal/command environment.
  • A realistic vector is a malicious repository with a project settings file (.zed/settings.json) that defines terminal.env.

What can an attacker do?

  • Execute arbitrary shell commands on the remote host as the user running the remote terminal/session
  • Modify files, exfiltrate data accessible to that user, install persistence in that user account, etc.

Recommended fix

I think the safest fix is to validate environment variable names before formatting them into shell command strings.

Suggested approach

Reject env keys that do not match a strict identifier pattern, for example:

^[A-Za-z_][A-Za-z0-9_]*$

This should be applied before interpolation in:

  • SSH POSIX command builder
  • WSL command builder

Why validation is preferable here

  • Quoting env keys correctly is shell-specific and easy to get wrong
  • Environment variable names have a narrow valid character set anyway
  • Validation is simpler and less error-prone than escaping arbitrary key strings

Additional hardening recommendations

  • Add regression tests for invalid env keys containing:
      - $(...)
      - backticks (`...`)
      - ;
      - spaces
      - =
  • Consider validating env keys earlier when loading project terminal settings, so invalid keys are rejected consistently

References

Patches

  • Fixed in >=v0.227.1

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

CVE ID

CVE-2026-44461

Weaknesses

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component. Learn more on MITRE.

Credits