Security-specific Programming Errors (Part 8)
Thomas Biege
Table of Contents
Perl
In general, Perl is used to perform small to medium-sized tasks. Perl's major advantage is that it processes characters with the help of regular expressions. The tasks performed by Perl scripts are thus similar to those performed by shell scripts. In fact, Perl scripts are used significantly more often than shell scripts with CGI. In addition, when using modules, Perl can also perform many tasks such as network communication (client/server). This means its versatility makes Perl vulnerable to a wide range of hazards from different sources.
Temporary Files
Perl does not provide a dedicated option for creating temporary files. Unfortunately, users frequently use the open() command to create temporary files, a practice that renders a Perl script vulnerable to attacks (link attacks, race conditions).
Using your own directories is a secure alternative, as described in the section on shell scripts, as well as using the sysopen() function. Sysopen() works exactly like the open(2) system call.
For example:
[...]
use Fcntl qw(O_RDWR O_CREAT O_EXCL);
[...]
sysopen(TMPFILE, "/tmp/myfile.666",
O_RDWR|O_CREAT|O_EXCL, 0600);
[...]
In other words, the problem and its solution are exactly the same as those for C.
Dangerous Perl Commands and Properties
Many Perl commands have undesirable properties that can make it easy for an attacker to spot security flaws.
Below is a list of all security-specific commands and properties.
To perform a task, you should always try to use those commands that do not utilize the shell. Once data that is not trustworthy reaches the shell level, all bets are off.
System() and exec() behave similarly. Both commands also use the shell if they are called with a single argument only. If an attacker can define this argument, he then has the ability to use shell meta characters to start any command of his choice.
Wrong:
system("ls -al /home/$username");
If the attacker enters the $username "evil;cat /etc/shadow", the system outputs the file /etc/shadow.
Right:
system("ls", "-al", "/home/$username");
Unfortunately, the attacker can still use ../../../../ to select a file of his choice for use.
The best solution is to select a filter for Perl, just like for shell, which only accepts authorized characters.
unless($userinput =~ tr/[a-zA-z0-9@]//)
{
print "Nice try, pal!\n";
exit(1);
}
The functions glob(),<> and backticks utilize the shell as well and should not be supplied directly with user data.
In conjunction with the pipe symbol, Perl's open() command can use programs like it uses files.
For example:
[...]
open(LPD, "| lpd");
[...]
In this example, the BSD printer spooler client opens for writing in order to print a file. This property of the open() command makes it possible for an attacker to simply enter a pipe symbol followed by a program name as the argument for open() in order to execute this program.
We can use the following code to emulate a secure popen(3):
[...]
open(PIPE, "-|") || exec("/bin/ls", $userdate);
print while <PIPE>;
[...]
Still, this does not eliminate the risk of the user moving back through the directory tree via ../.
You can dynamically execute Perl commands during a Perl script's runtime by using the eval() command or the modifier /e for regular expressions. This modifier processes an expression prior to analysis. This means that if user input is passed to eval() or /e, an attacker can instruct the system to execute Perl code according to his strategy. Character filters are not necessarily effective in preventing such an attack.
Perl frequently uses the shell to call external programs, which leaves the system vulnerable to attacks targeting the environment variables $PATH, $IFS etc. If you want to run the Perl script with a higher level of rights, we recommend that you clean out the programming environment and then reset the entire environment, or you should use full path names and avoid the shell.
For example:
[...]
$ENV{PATH} = join ':' => split(" ", << '__PATHEND__');
/bin/
/sbin/
/usr/bin/
/usr/sbin/
__PATHEND__
[...]
Also note that the @INC variable is a special case. This variable specifies the path to the Perl modules. If the attacker defines the variable so that the path includes his modules, he can instruct the system to execute his code. Thus, you should assign a secure value to the @INC variable before integrating modules with the use command.
The so-called poison NUL byte (a term also used for one-byte buffer overflows) describes a property of Perl in connection with C. Unlike C, Perl does not interpret the 0x00 character as the end of a character string. An attacker can exploit this property by entering a 0x00 character at the appropriate position.
For example:
[...]
$username = param("username");
[...]
open(PROFILE, "-|") || exec("/usr/local/bin/my_txt2html",
"/etc/$username.profile");
print while ;
[...]
This CGI script enables you to retrieve user profiles located in /etc (which is highly unlikely). If the attacker now enters profile.pl&username=shadow%00, my_txt2html does not open /etc/shadow0x00.profile, but /etc/shadow instead. Once again, the only solution to this problem is to filter for authorized characters.
It is important to remove backslashes from the user input. User input backslashes negate subsequent backslashes, such as, for example, those inserted by your Perl script to minimize the damage caused by dangerous entries. There is the potential of similar hazards caused by other characters.
To clarify once more: You should not filter out potentially dangerous characters. Instead, you should simply allow authorized characters only.
If a Perl script, in defiance of common sense, has been set to SetUID or SetGID, you must not activate the special privileges unless they are truly needed and those sections should be as small as possible.
For example:
[...]
# drop privileges
$) = $( # eGID = rGID
$> = $
< # eUID = rUID
[...]
# gain back higher privileges
$( = $) # rGID = eGID
$< = $> # rUID = eUID
[...]
Perl scripts with special tasks and privileges should be executed in taint mode. To do so, perl simply uses the -T option. The taint mode, of course, can mitigate only a few of Perl's security risks and cannot do the thinking for the programmer.
Signals
As far as signals are concerned, Perl is subject to the same threats as C and shell languages.
You can use the following procedure to block or capture signals:
[...]
sub SayGoodBye
{
print "Good Bye!\n\n";
exit(0);
}
[...]
$SIG{'HUP'} = 'SayGoodBye';
$SIG{'INT'} = 'SayGoodBye';
$SIG{'QUIT'} = 'SayGoodBye';
$SIG{'ILL'} = 'SayGoodBye';
$SIG{'TRAP'} = 'SayGoodBye';
$SIG{'ABRT'} = 'SayGoodBye';
$SIG{'UNUSED'} = 'SayGoodBye';
$SIG{'FPE'} = 'SayGoodBye';
$SIG{'KILL'} = 'SayGoodBye';
$SIG{'USR1'} = 'SayGoodBye';
$SIG{'SEGV'} = 'SayGoodBye';
$SIG{'USR2'} = 'SayGoodBye';
$SIG{'PIPE'} = 'SayGoodBye';
$SIG{'ALRM'} = 'SayGoodBye';
$SIG{'TERM'} = 'SayGoodBye';
$SIG{'STKFLT'} = 'SayGoodBye';
$SIG{'IO'} = 'SayGoodBye';
$SIG{'XCPU'} = 'SayGoodBye';
$SIG{'XFSZ'} = 'SayGoodBye';
$SIG{'VTALRM'} = 'SayGoodBye';
$SIG{'PROF'} = 'SayGoodBye';
[...]
Tools
Sources
- Bugtraq@securityfocus.com
- Secprog@securityfocus.com
- Security-Audit@*ferret*uk
- Aleph One; Smashing the Stack for Fun and Profit; Pharck49-7
- W. Richard Stevens; Advanced Programming in the UNIX® Environment; Addison Wesley
- W. Richard Stevens; UNIX® Network Programming; Prentice Hall
- Trutz Eyke Podschun; Das Assembler-Buch (The Assembly Book); Addison Wesley
- Matt Bishop; Unix Security: Writing Secure Programs; SANS '96
- Gerhard Willms; Das C-Grundlagen Buch (The C Basics Book); Data Becker
- perlsec(1) Man Page
|