The Simson & Garfinkel's “Practical UNIX & Internet Security” (2nd edition, 1996) mentions an attack against SUID/SGID binaries involving duplicate environment variables. In essence, this is an interpretation conflict between different environment variable list parsers (getenv, setenv, putenv, iterating through environ, looking at the envp argument to main). Some implementations will pick the first variable, some will pick the last.
We should traverse the environment very early at process startup and normalize it, removing duplicates.
Related security advisory for CVE-2016-2381:
To add a bit of context, in:
setenv("PATH", "sane-value", 1);
in an executable (like a setuid/setgid executable) that has been called with envp having both "PATH=whatever" and "PATH=evil-path", setenv() will update the first "PATH" entry, but leave the other ones untouched and system() will execute sh with both "PATH=sane-value" and "PATH=evil-path" in its envp.
Some sh implementations like ksh93 or zsh will consider the first env entry when there are duplicates, some like bash, dash, yash, mksh will consider the last. Some will remove duplicates, some will not. perl used to get the last when filling in %ENV upon initialisation but set the first upon assigning %ENV entries (also fooling taint-mode safeguards) and has now been fixed.
When discussed privately among shell and perl maintainers (and Debian/Redhat/Suse seclists), bash, mksh and dash maintainers have declined changing their shell so they get the first env var entry, arguing that the issue should be fixed at the libc or kernel level. ksh93 and zsh are not affected.
Also note that "sudo"'s maintainer has also agreed to filter duplicates as that could be exploited for variables that sudo lets through like LC_*. https://www.sudo.ws/repos/sudo/rev/d4dfb05db5d7 (no released yet).
Good points. The glibc normalization code should pick the first defined variable as well, for consistency's sake.