Campione 1996 suggests:
To maximize portability, never refer to an environment variable when the same value is available in a system property. For example, if the operating system provides a user name, it will always be available in the system property user.name.
In fact, relying on environment variables is more than a simple portability issue. An attacker can essentially control all environment variables that enter a program, using a mechanism such as the java.lang.ProcessBuilder
class.
Noncompliant Code Example
This noncompliant code example tries to get the user name, using an environment variable.
Code Block | ||
---|---|---|
| ||
String username = System.getenv("USER");
|
This certainly is a portability issue. Campione 1996 further suggests:
The way environment variables are used also varies. For example, Windows provides the user name in an environment variable called USERNAME, while UNIX implementations might provide the user name in USER, LOGNAME, or both.
Furthermore, an attacker can execute this program with the USER
environment variable set to any value he chooses. The following code example does just that:
Code Block | ||
---|---|---|
| ||
public static void main(String args[] |
The getenv()
function searches an environment list for a string that matches a specified name and returns a pointer to a string associated with the matched list member.
Wiki Markup |
---|
Section 7.20.4.5 of C99 states that \[[ISO/IEC 9899:1999|AA. Bibliography#ISO/IEC 9899-1999]\] |
The set of environment names and the method for altering the environment list are implementation-defined.
Depending on the implementation, multiple environment variables with the same name may be allowed and can cause unexpected results if a program cannot consistently choose the same value. The GNU glibc library addresses this issue in getenv()
and setenv()
by always using the first variable it encounters and ignoring the rest. However, it is unwise to rely on this.
Wiki Markup |
---|
One common difference between implementations is whether or not environment variables are case sensitive. While UNIX-like implementations are generally case sensitive, environment variables are "not case sensitive in Windows 98/Me and Windows NT/2000/XP" \[[MSDN|AA. Bibliography#MSDN]\]. |
Duplicate Environment Variable Detection (POSIX)
The following code defines a function that uses the POSIX environ
array to manually search for duplicate key entries. Any duplicate environment variables are considered an attack, so the program immediately terminates if a duplicate is detected.
Code Block | ||
---|---|---|
| ||
extern char **environ; int main(void) { if (multiple_vars_with_same_name())args.length != 1) { printfSystem.err.println("Someone may be tampering.\nPlease supply a username as the argument"); return 1; } /* ... */ return 0; } int multiple_vars_with_same_name(void) { size_t i; size_t j; size_t k; size_t l; size_t len_i; size_t len_j; for(size_t i = 0; environ[i] != NULL; i++) String user = args[0]; ProcessBuilder pb = new ProcessBuilder(); pb.command("/usr/bin/printenv"); Map<String,String> environment = pb.environment(); environment.put("USER", user); pb.redirectErrorStream(true); try { for(size_t j = i; environ[j] != NULL; j++) { if (i != j) { k = 0Process process = pb.start(); InputStream in l = 0; len_i = strlen(environ[i]= process.getInputStream(); len_j = strlen(environ[j]); int c; while (k(c < len_i && l < len_j) { if (environ[i][k]= in.read()) != environ[j][l]) break; -1) { if (environ[i][k] == '=') return 1; k++;System.out.print((char) c); } int exitVal l++= process.waitFor(); } catch (IOException x) }{ } } return 0; } |
Noncompliant Code Example
The following noncompliant code behaves differently when compiled and run on Linux and Microsoft Windows platforms.
Code Block | ||
---|---|---|
| ||
if (putenv("TEST_ENV=foo") != 0) { /* Handle error */ } if (putenv("Test_ENV=bar") != 0x.printStackTrace(System.err); } catch (InterruptedException x) { /* Handle error */ } const char *temp = getenv("TEST_ENV" x.printStackTrace(System.err); if (temp == NULL) { /* Handle error */ } printf("%s\n", temp); |
On an IA-32 Linux machine with GCC Compiler Version 3.4.4, this code prints
Code Block |
---|
foo
|
whereas, on an IA-32 Windows XP machine with Microsoft Visual C++ 2008 Express, it prints
Code Block |
---|
bar
|
Compliant Solution
}
}
|
This program runs the program /usr/bin/printenv
which prints out all environment variables and their values. It takes a single argument string, and sets the USER
environment variable to that string. The subsequent output of the printenv
program will indicate that the USER
environment variable is indeed set to the string requested.
Compliant Solution
This compliant solution obtains the user name using the user.name
system property. This property always contains the correct user name, even if the USER
environment variable has been set to an incorrect value, or does not existPortable code should use environment variables that differ by more than capitalization.
Code Block | ||
---|---|---|
| ||
if (putenv("TEST_ENV=foo") != 0) { /* Handle error */ } if (putenv("OTHER_ENV=bar") != 0) { /* Handle error */ } const char *temp = getenv("TEST_ENV"); if (temp == NULL) { /* Handle error */ } printf("%s\n", tempString username = System.getProperty("user.name"); |
Risk Assessment
An attacker can create multiple environment variables with (for example, by using the POSIX execve()
function). If the program checks one copy but uses another, security checks may be circumventedA program that depends on environment variables may be fed misinformation by an attacker.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ENV02-J | low | unlikely likely | medium low | P2 P9 | L3 L2 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
ENV02-C. Beware of multiple environment variables with the same effective name | |
ENV02-CPP. Beware of multiple environment variables with the same effective name | |
Section 7.20.4, "Communication with the Environment" | |
"XYS Executing or Loading Untrusted Code" | |
CWE-462, "Duplicate Key in Associative List (Alist)" | |
CWE-807, "Reliance on Untrusted Inputs in a Security Decision" |
Bibliography
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="729ae115-152a-4fe3-84aa-a7d9a6e0e71c"><ac:plain-text-body><![CDATA[ | [[API 2006 | AA. Bibliography#API 06]] | ]]></ac:plain-text-body></ac:structured-macro> | ||
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="2296c6709da61747-41490f34-48ef4be3-bfbe82b3-6306427676482f0b5ce79309"><ac:plain-text-body><![CDATA[ | [[MSDNCampione 1996 | AA. Bibliography#MSDNBibliography#Campione 96]] | [ | http://msdn.microsoft.com/en-us/library/tehxacec(VS.71).aspx] | ]]></ac:plain-text-body></ac:structured-macro> |
...