March 7, 2021
Hot Topics:

Security Issues in Perl Scripts

  • By Jordan Dimov
  • Send Email »
  • More Articles »

setuid scripts

Normally a Perl program runs with the privileges of the user who executed it. By making a script setuid, its effective user ID can be set to one that has access to resources to which the actual user does not (viz., to the owner ID of the file containing the program). The passwd program for example uses setuid to acquire writing permission to the system password file, thus allowing users to change their own passwords. Since programs that are executed via a CGI interface run with the privileges of the user who runs the web server (usually this is user 'nobody', who has very limited privileges), CGI programmers are often tempted to use the setuid technique to let their scripts perform tricks that they otherwise couldn't. This can be useful, but it can also be very dangerous. For one thing, if an attacker finds a way to exploit a weakness in the script, they won't only gain access to the system, but they will also have it with the privileges of the effective UID of that script (often the 'root' UID).

To avoid this, Perl programs should set the effective UID and GID to the real UID and GID of the process before any file manipulations:

\begin{verbatim}     $> = $< # set effective user ID to real UID.     $) = $( # set effective group ID to real GID.

and CGI scripts should always run with the lowest possible privilege.

Beware that just being careful in what you do inside your setuid script doesn't always solve the problem. Some operating systems have bugs in the kernel that make setuid scripts inherently insecure. For this, and other reasons, Perl automatically switches to a special security mode (taint mode) when it runs setuid or setgid scripts. We will discuss taint mode in our next article.


Generating random numbers on deterministic machines is a nontrivial problem. In security critical applications, random numbers are used intensely for many important tasks ranging from password generation to cryptography. For such purposes, it is vital that the generated numbers are as close to truly random as possible, making it difficult (but never impossible) for an attacker to predict future numbers generated by the algorithm. The Perl rand() function simply calls the corresponding rand(3) function from the standard C library. This routine is not very secure. The C rand() function generates a sequence of pseudorandom numbers based on some initial value called the seed. Given the same seed, two different instances of a program utilizing rand() will produce the same random values. In many implementations of C, and in all version of Perl before 5.004, if a seed is not explicitly specified, it is computed from the current value of the system timer, which is anything but random. Having some information about values produced by rand() at a given point and a sufficient amount of time, any self-respecting cracker can accurately predict the sequence of numbers that rand() will generate next, thus obtaining key knowledge necessary to compromise a system.

One (partial) solution to the rand() problem is to use one of the built-in random number generators on Linux systems -- /dev/random and /dev/urandom. Those are better sources of randomness then the standard library rand() function, but like anything else, they have their own imperfections. The difference between the two devices is that /dev/random stops supplying random numbers when its entropy pool runs out of randomness while /dev/urandom uses cryptography to generate new numbers when the entropy pool runs out. Another solution is to use a secure implementation of one of the more complicated cryptographic random number generators such as Yarrow.

Race Conditions

Race conditions (together with buffer overflows) are a favorite of seasoned crackers. Consider the following code:

     unless (-e "/tmp/a_temporary_file") {       open (FH, ">/tmp/a_temporary_file");     }

At first glance this is a very legitimate piece of code that doesn't seem capable of causing any harm. We check to see whether the temporary file exists, and if it doesn't we tell Perl to create it and open it for writing. The problem here is that we assume that our e check is correct at the time we open the file. Of course, Perl wouldn't lie to us about a file existence, but unlikely as it might seem, it is entirely possible that the status of our file has changed between the time we check for it and the time we open it for writing. Suppose that the temporary file does not exist. Suppose also that a knowledgeable attacker, familiar with the workings of our program, executed the following command right at the time after we did our existence check:

    ln -s /tmp/a_temporary_file /etc/an_important_config_file

Now everything we do to the temporary file actually gets done to that important config file of ours. Since we believe that the temp file does not exist (that's what our --e check told us), we go ahead and open it for writing. As a result, our config file gets erased. Not very pleasant. And if the attacker knew what they're doing, this might even be fatal.

Situations like this, where an attacker can race in and change something to cause us trouble between two actions of our program are known as race conditions. In this particular case we have a TOCTOU (Time-Of-Check-Time-Of-Use) race condition. There are several other similar types of race conditions. Such imperfections in a program are very easy to overlook even by experienced programmers, and are being actively exploited. There is no easy omni-powerful solution to this problem. Often the best approach is to use atomic operations when the possibility of race conditions exists. This means using only one system call to do a check for a file and to create that file at the same time, without giving the processor the opportunity to switch to another process in between. This is not always possible though. Another thing we could do in our example would be to use sysopen() and specify a write-only mode, without setting the truncate flag:

     unless (-e "/tmp/a_temporary_file") {       #open (FH, ">/tmp/a_temporary_file");        sysopen (FH, "/tmp/a_temporary_file", O_WRONLY);       }

This way even if our filename does get forged, we won't kill the filewhen we open it for writing.

Note: the module Fcntl must be included in order for that sysopen() call to work, because this is where the constants O_RDONLY, O_WRONLY, O_CREAT, etc. are defined.

Buffer Overflows and Perl

In general, Perl scripts are not susceptible to buffer overflows because Perl dynamically extends its data structures when needed. Perl keeps track of the size and allocated length of every string. Before each time a string is being written into, Perl ensures that enough space is available, and allocates more space for that string if necessary.

There are however a few known buffer overflow conditions in some older implementations of Perl. Notably, version 5.003 can be exploited with buffer overflows. All versions of suidperl (a program designed to work around race conditions in setuid scripts for some kernels) built from distributions of Perl earlier than 5.004 are BO exploitable (CERT Advisory CA--97.17).


In our follow-on article, we will spend some time getting acquainted with the security features that Perl has to offer, particularly Perl's "taint mode", and we'll try to identify some problems that can slip through this tightened security if we are not careful. In studying those aspects of Perl and looking at some characteristic examples, our goal will be to develop an intuition that will help us recognize security problems in Perl scripts at first glance and avoid making similar mistakes in our programs.


Rain Forest Puppy, Perl CGI problems, Phrack Magazine, Vol. 9, Issue 55, File 07.

The World Wide Web Security FAQ. Chapter 7 -- Safe Scripting in Perl. http://www.w3c.org/Security/Faq/wwwsf5.html

The Perl Security man page.

CGI Programming with Perl, 2nd Edition. O'Reilly and Associates. July 2000.

the ITS4 Software Security Scanner. http://www.cigital.com/its4/

The SANS institute's list of top-ten most-critical internet security threats. http://www.sans.org/topten.htm

Matt Bishop, Michael Dilger. Checking for Race Conditions in File Accesses. Computing Systems 9(2), Spring 1996, pp. 131-152.

Page 3 of 3

This article was originally published on February 6, 2001

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date