GitHub
CERT Secure Coding

IDS31-PL. Do not use the two-argument form of open()

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 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 file name to be opened or shell command to be executed.

If an attacker can provide a file name argument to be used in the two-argument form of open() , 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() .

Non-compliant code
my $filename = # initialize
open(my $FILE, $filename) or croak("file not found");
while (<$FILE>) {
  print "$filename: $_";
};

Although this code clearly expects its file to be opened for reading, the 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 file name.

Non-compliant code
my $filename = # initialize
open(my $FILE, "<$filename") or croak("file not found");
while (<$FILE>) {
  print "$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 file name, which is interpreted by open() as reading standard input.

Noncompliant Code Example ( <ARGV> )

This noncompliant code example uses the <ARGV> operator.

Non-compliant code
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 is interpreted as a shell command and executed.

Noncompliant Code Example ( <> )

This noncompliant code example uses the <> operator, known as the diamond operator.

Non-compliant code
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.

Non-compliant code
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 is interpreted as a shell command and executed. In this manner, the -n operator acts exactly like the two-argument form of open() .

Noncompliant Code Example ( -p )

This noncompliant code example uses the -p argument to Perl.

Non-compliant code
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 is interpreted as a shell command and executed. In this manner, the -n operator acts exactly like the two-argument form of open() .

Compliant Solution

This compliant solution invokes open() with three arguments rather than two.

Compliant code
my $filename = # initialize
open(my $FILE, "<", $filename) or croak("file not found");
while (<$FILE>) {
  print "$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 named - rather than opening standard input.

Compliant Solution ( <<>> )

This compliant solution uses the <<>> operator, known as the double diamond operator.

Compliant code
while (<<>>) {
  print ":: $_";
};

The <<>> operator works like the <> operator except for using the three-argument form of open() (with "<" as the second argument) to accomplish this task.

Risk Assessment

Failure to handle error codes or other values returned by functions can lead to incorrect program flow and violations of data integrity.

Rule Severity Likelihood Detectable Repairable Priority Level
IDS31-PL high likely Yes No P18 L1

Automated Detection

Tool

Version

Checker

Description

Perl::Critic

5.0

InputOutput::ProhibitTwoArgOpen

Implemented

B::Lint

5.0

Use of <>
Unterminated <> operator

Implemented

Security Reviewer - Static Reviewer

6.02

PERL_D88

Implemented

Bibliography

[ VU#272296 ]AWStats fails to properly filter user-supplied input
[ VU#453475 ]PGPMail.pl does not adequately validate user input thereby allowing arbitrary command execution
[ VU#496064 ]ibrow NewsDesk does not securely handle input passed to open()
[ VU#671444 ]Input validation error in quikstore.cgi allows attackers to execute commands
[ Wall 2011 ]perlfunc