Security Risks of AcceptEnv * in SSH: Environment Variable Exploits and Secure Alternatives


3 views

The AcceptEnv directive in /etc/ssh/sshd_config controls which client environment variables get propagated to the server session. While convenient for configuration management, its unrestricted use (AcceptEnv *) introduces significant security vulnerabilities.

Three primary attack vectors emerge from unrestricted environment variable acceptance:


# Example of dangerous variables that could be injected:
AcceptEnv LD_PRELOAD PATH IFS LANG TZ SSH_*
  • LD_PRELOAD Hijacking: Attackers can load malicious libraries by setting LD_PRELOAD before executing privileged operations
  • PATH Manipulation: Modified PATH variables can redirect command execution to attacker-controlled binaries
  • Shell Injection: Variables like IFS (Internal Field Separator) can alter shell parsing behavior

Consider this attack chain when AcceptEnv * is enabled:


# Attacker's client-side command:
ssh -o SendEnv=LD_PRELOAD server.example.com

# Malicious environment being set:
export LD_PRELOAD=/tmp/evil.so

The attacker's shared object gets loaded into every process spawned by SSH, potentially compromising the entire session.

Instead of wildcard acceptance, explicitly enumerate required variables:


# /etc/ssh/sshd_config
AcceptEnv LANG LC_* MY_APP_CONFIG SSH_CONNECTION

For dynamic environment needs, consider these alternatives:

Option 1: ForcedCommand with Environment Filtering


Match User deploy-user
  ForceCommand /usr/local/bin/env-filter.sh

Where env-filter.sh contains:


#!/bin/bash
# Only allow safe variables
export ALLOWED_VARS="VAR1 VAR2 VAR3"
for var in $ALLOWED_VARS; do
    [ -n "${!var}" ] && export $var="${!var}"
done
exec "${SSH_ORIGINAL_COMMAND:-$SHELL}"

Option 2: SSH AuthorizedKeysCommand with Variable Validation


# ~/.ssh/authorized_keys
command="/usr/local/bin/validate-env" ssh-rsa AAAAB3...

The validation script:


#!/usr/bin/env python3
import os
import sys

ALLOWED_ENV = {'DEPLOY_ENV', 'BUILD_ID'}

def main():
    clean_env = {k:v for k,v in os.environ.items() 
                if k in ALLOWED_ENV}
    # Spawn shell with cleaned environment
    os.execve('/bin/bash', ['bash', '-i'], clean_env)

if __name__ == '__main__':
    main()

To identify currently accepted variables across your infrastructure:


# Find all sshd_config files accepting environment variables
find /etc/ssh/ -name sshd_config -exec grep -l 'AcceptEnv' {} \;

# Check which variables are actually being sent
ssh -vvv user@host 2>&1 | grep -i "sending env"

Remember that any variable accepted becomes part of the server's attack surface. Periodically review your accepted variables just as you would review user permissions.


When configuring OpenSSH servers, the AcceptEnv directive in sshd_config controls which client environment variables get transferred to the server session. While convenient, using AcceptEnv * creates significant security vulnerabilities:


# DANGEROUS configuration in /etc/ssh/sshd_config:
AcceptEnv *

Attackers can manipulate environment variables to:

  • Inject malicious library paths via LD_PRELOAD/LD_LIBRARY_PATH
  • Override critical paths using PATH manipulation
  • Bypass security controls through TERM or SHELL hijacking
  • Leak sensitive information via SSH_* variables

Consider this attack sequence when AcceptEnv * is enabled:


# Attacker's malicious environment
export LD_PRELOAD=/tmp/evil.so
export PATH=/malicious/bin:$PATH

# SSH connection with poisoned environment
ssh -o SendEnv=* vulnerable-server.example.com

The server would then load the attacker's shared library and use their compromised PATH.

Instead of using *, explicitly list required variables:


# Secure configuration in /etc/ssh/sshd_config:
AcceptEnv LANG LC_* MY_APP_CONFIG PROJECT_ID DEPLOY_ENV

For dynamic variables, consider these approaches:


# Client-side script to extract needed variables
env | grep -E '^APP_|^DEPLOY_' | awk '{print "SendEnv", $1}' > ssh_config_snippet

# Server-side filtering script in ~/.ssh/rc
for var in ${!APP_*}; do
    # Validate and sanitize each variable
    [[ ${!var} =~ ^[a-zA-Z0-9_=/.-]+$ ]] || unset "$var"
done
  • Maintain an allowlist of approved variables
  • Validate variable contents server-side
  • Consider using SSH ForceCommand wrappers for validation
  • Log and monitor unusual environment variable patterns

Remember that environment variables form part of the attack surface. Treat them with the same security consideration as other input vectors.