
Many programs and libraries, including the shared library loader on both Unix UNIX and Windows systems, depend on environment variable settings. Because environment variables are inherited from the parent process when a program is executed, an attacker can easily sabotage variables, causing a program to behave in an unexpected and insecure manner \[ [Viega 03|AA. C References#Viega 03]\2003]. Wiki Markup
All programs, in particular particularly those running with higher privileges than the caller (such as those with setuid/setgid
flags), should treat their environment as untrusted user input. Since Because the environment is inherited by processes spawned by calls to the fork()
, system()
, or exec()
functions, it is important to verify that the environment does not contain any values that could can lead to unexpected behavior.
The best practice for setuid/setgid
such programs is to:
- drop Drop privileges once they are no longer necessary. (see See POS02-AC. Follow the principle of least privilege.)
- avoid Avoid calling
system()
. (see ENV04See ENV33-AC. Do not call system() if you do not need a command processor.) - Clear the environment and fill it ) if a command processor in absolutely necessary, the environment should be cleared out and filled with trusted or default values (see .
This recommendation is a more specific instance of STR02-
...
C. Sanitize data passed to complex subsystems
...
.
Subclause 7.22.4.6 of the C Standard states that "the Because the C99 standard states that "The set of environment names and the method for altering the environment list are implementation-defined." It Consequently, it is important to understand what local which functions are available for clearing, modifying, and looking up default values for environment variables. Since Because some programs may behave in unexpected ways when certain environment variables are not set, it is important to understand which variables are necessary on your system and what are safe values for them.
The POSIX.9 function clearenv()
may be used to clear out the environment, or where not available, setting environ
to NULL should accomplish the same effect.
Wiki Markup |
---|
The proposed POSIX.2 standard also specifies the {{confstr()}} function which can then be used to look up default values for environment variables. POSIX.1-2008 \[[Austin Group 08|AA. C References#Austin Group 08]\] defines a new {{\_CS_V7_ENV}} argument to {{confstr()}} to retrieve a list of environment variable settings required for a default conforming environment. A space-separated list of variable=value pairs is returned, with variable names guaranteed not to contain = signs, and variable=value pairs guaranteed not to contain spaces. Used together with the {{\_CS_PATH}} request illustrated above, this completely describes the minimum environment variable settings required to obtain a clean conforming environment. On systems conforming to the POSIX.1-2008 standard, this should be used to create a sanitized environment. |
Wiki Markup |
---|
If you explicitly know which environment variables you want to keep, \[[Viega 03|AA. C References#Viega 03]\] defines a function which will remove everything else. |
Non-Compliant Code Example (POSIX) (ls)
Noncompliant Code Example (POSIX, ls
)
This noncompliant code example invokes the C This non-compliant code invokes the C99 system()
function to execute the /bin/ls
program. The C99 system()
function passes a string to the command processor in the host environment to be executed.
Code Block | ||||
---|---|---|---|---|
| ||||
if ( system("/bin/ls dir.`date +%Y%m%d`");) == -1) { /* Handle error */ } |
Although IFS
does not affect the command portion of this string, /bin/ls
, it does determine how the argument is built after calling date
. If the default shell does not ignore the incoming value of the IFS}}
environment value, and an attacker sets {{IFS
to "."
, the intended directory will not be found.
Compliant Solution (POSIX, ls
)
...
The nonstandard function clearenv()
may be used to clear out the environment where available: otherwise, the environment can be cleared by obtaining the environment variable names from environ
and removing each one using unsetenv()
.
In this compliant solution, the environment is cleared by clearenv()
, and then the PATH
and IFS
variables are set to safe values before system()
is invoked. Sanitizing shell commands can be difficult, and doing so can adversely affect the power and flexibility associated with them.
Code Block | ||||
---|---|---|---|---|
| ||||
char *pathbuf; size_t n; if (clearenv() != 0) { /* Handle Errorerror */ } n = confstr(_CS_PATH, NULL, 0); if (n == 0) { /* Handle error */ } if ((pathbuf = malloc(n)) == NULL) { /* Handle Errorerror */ } if (confstr(_CS_PATH, pathbuf, n); == 0) { /* Handle error */ } if (setenv("PATH", pathbuf, 1) == -1) { /* Handle Errorerror */ } if (setenv("IFS", " \t\n", 1) == -1) { /* Handle Errorerror */ } if (system("/bin/ls dir.`date +%Y%m%d`") == -1) { /* Handle Errorerror */ } |
POSIX also specifies the confstr()
function, which can be used to look up default values for environment variables [IEEE Std 1003.1:2013]. The _CS_V7_ENV
argument to confstr()
retrieves a list of environment variable settings required for a default conforming environment [IEEE Std 1003.1:2013]. A space-separated list of variable=value
pairs is returned, with variable names guaranteed not to contain equal signs (=), and variable=value
pairs guaranteed not to contain spaces. Used together with the _CS_PATH
request, this completely describes the minimum environment variable settings required to obtain a clean, conforming environment. On systems conforming to the POSIX.1-2008 standard, this should be used to create a sanitized environment.
On systems that have no clearenv()
function, the following implementation can be used:
Code Block | ||||
---|---|---|---|---|
| ||||
extern char **environ;
int clearenv(void) {
static char *namebuf = NULL;
static size_t lastlen = 0;
while (environ != NULL && environ[0] != NULL) {
size_t len = strcspn(environ[0], "=");
if (len == 0) {
/* Handle empty variable name (corrupted environ[]) */
}
if (len > lastlen) {
namebuf = realloc(namebuf, len+1);
if (namebuf == NULL) {
/* Handle error */
}
lastlen = len;
}
memcpy(namebuf, environ[0], len);
namebuf[len] = '\0';
if (unsetenv(namebuf) == -1) {
/* Handle error */
}
}
return 0;
}
|
Compliant Solution (Windows)
There is no portable or guaranteed way to clear out the environment under Windows. Following ENV33-C. Do not call system(), care should be taken to use _execle()
, _execlpe()
, _execve()
, or _execvpe()
instead of system()
, because they allow the environment to be explicitly specified.
If it is explicitly known which environment variables need to be kept, Secure Programming Cookbook for C and C++ [Viega 2003] defines a function, spc_sanitize_environment()
, that will remove everything elseSanitizing a shell command can be difficult, and secure results can adversely impact the inherent power and flexibility associated with shell commands.
Risk Assessment
Invoking an external program in an attacker-controlled environment is inherently dangerous.
Recommendation | Severity | Likelihood | Detectable |
---|
Repairable | Priority | Level |
---|---|---|
ENV03- |
C | High |
Likely |
No |
No |
P9 | L2 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Helix QAC |
| C5017 | |||||||
LDRA tool suite |
| 588 S | Partially implemented |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[Austin Group 08|AA. C References#Austin Group 08]\] vol. 2, System Interfaces, {{confstr()}}
\[[CA-1995-14|http://www.cert.org/advisories/CA-1995-14.html]\] "Telnetd Environment Vulnerability"
\[[Dowd 06|AA. C References#Dowd 06]\] Chapter 10, "UNIX II: Processes"
\[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.20.4, "Communication with the environment"
\[[Open Group 04|AA. C References#Open Group 04]\] Chapter 8, "Environment Variables"
\[[Viega 03|AA. C References#Viega 03]\] Section 1.1, "Sanitizing the Environment"
\[[Wheeler 03|AA. C References#Wheeler 03]\] [Section 5.2, "Environment Variables"|http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html] |
Related Guidelines
SEI CERT C++ Coding Standard | VOID ENV01-CPP. Sanitize the environment when invoking external programs |
CERT Oracle Secure Coding Standard for Java | IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method |
ISO/IEC TR 24772:2013 | Executing or Loading Untrusted Code [XYS] |
MITRE CWE | CWE-78, Failure to sanitize data into an OS command (aka "OS command injection") CWE-88, Argument injection or modification CWE-426, Untrusted search path CWE-471, Modification of Assumed-Immutable Data (MAID) CWE-807, Reliance on intrusted inputs in a security decision |
Bibliography
[CA-1995-14] | "Telnetd Environment Vulnerability" |
[Dowd 2006] | Chapter 10, "UNIX II: Processes" |
[IEEE Std 1003.1:2013] | Chapter 8, "Environment Variables" XSH, System Interfaces, confstr |
[ISO/IEC 9899:2011] | Subclause 7.22.4, "Communication with the Environment" |
[Viega 2003] | Section 1.1, "Sanitizing the Environment" |
[Wheeler 2003] | Section 5.2, "Environment Variables" |
...
ENV02-A. Beware of multiple environment variables with the same name 10. Environment (ENV) ENV04-A. Do not call system() if you do not need a command processor