Safeguard
Application Security•

CWE-154 Variable Name Delimiters: How to Stop Expansion Attacks

A dollar sign or percent sign in input can be expanded into a variable you never meant to expose. How CWE-154 happens and how to fix it.

Safeguard Security Team
3 min read

CWE-154 describes input that contains characters a downstream component reads as variable name delimiters. MITRE's example is the dollar sign that starts an environment variable in a shell. When the delimiter arrives from an attacker, the downstream component swaps in the value of a variable the attacker picked.

Anyone who passes user text to a shell, a batch interpreter or a templating system that expands names is exposed. Windows percent variables count too.

How it happens

import os

# Vulnerable: the shell expands anything that looks like a variable
os.system("echo Hello " + username)

If username is $HOME or $SECRET_TOKEN, the shell prints the value. Worse, the same hole lets the attacker reach the command execution weaknesses that sit above this one.

The observed examples show expansion going wrong in other places. CVE-2005-0129 is a case where a percent variable is expanded by a wildcard function into disallowed commands. CVE-2002-0770 is a server that trusts the client to expand macros, letting macro characters trigger information exposure.

What an attacker gains

The record lists integrity impact as unexpected state. Expansion can expose values such as keys and tokens kept in the environment, or build commands the filter would have rejected had it seen the expanded form.

How to find it

MITRE lists no detection methods, so use these checks.

  • Grep for shell calls that take strings: os.system, shell=True, backticks, exec with a command string.
  • Send $VAR, %VAR% and similar markers through each input that reaches those calls.
  • Check whether validation runs before or after expansion. A filter that sees the unexpanded form can be bypassed.

How to fix it

  1. Do not hand text to a shell. Pass arguments as a list so no expansion happens.
import subprocess

# Fixed: no shell, no expansion
subprocess.run(["echo", "Hello", username], check=True)
  1. Anticipate that delimiters will be injected, removed or manipulated, and use an allowlist for fields such as names.
  2. If expansion is a feature, run validation on the expanded result, not just the raw input.
  3. Keep secrets out of the environment of processes that handle untrusted text where you can.

Quick check

Does a list of arguments stop variable expansion?

Yes for the shell, because no shell parses the string. The program you launch may still do its own expansion, so check its documentation.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.