[2512] in bugtraq
Re: CGI security: Escape newlines.
daemon@ATHENA.MIT.EDU (Robert S. Muhlestein)
Tue Feb 6 18:38:58 1996
Date: Tue, 6 Feb 1996 11:49:45 -0800
Reply-To: Bugtraq List <BUGTRAQ@CRIMELAB.COM>
From: "Robert S. Muhlestein" <robertm@teleport.com>
X-To: Lincoln Stein <lstein@kaa.crbm.cnrs-mop.fr>
To: Multiple recipients of list BUGTRAQ <BUGTRAQ@CRIMELAB.COM>
In-Reply-To: <9602061502.AA26447@beta.crbm.cnrs-mop.fr>
On Tue, 6 Feb 1996, Lincoln Stein wrote:
> In general I think that it's much better to search for and accept only
> the good patterns rather than trusting things to work when you exclude
> the bad characters. People forget that the shell isn't the only
> vulnerable program, and other programs may be subvertible by input
> quite distinct from the set of shell metacharacters.
>
> Lincoln
Ditto. I use a simply "check" routine to check every acceptable form
variable. Here are the subs I regularly use to accomplish this. If you
spot a bug, I'd appreciate a note. If you use my routines, please
leave a ref to me somewhere.
This example uses Lincoln's CGI.pm to grab form variables, then a few of
my own routines to check info, etc. Although I use these routines all
the time, I haven't tested the actual example script itself, beware:
__BEGIN__
#!/usr/local/bin/perl
########
## These routines are normally part of a package that I import:
########
$SENDMAIL = '/usr/lib/sendmail -t -n';
$DATE = localtime(time);
## To keep the perl jobs from taking priority over regular httpd daemons
setpriority(0,0,4);
#------------------------------------------------------------------------
sub log_it {
my $time = localtime($^T);
my $message = $_[0]; $message =~ tr/\n\"/ /;
($LOG = $_[1]) unless (defined $LOG);
my $addr = $ENV{'REMOTE_ADDR'} || 'LOCAL';
my $host = $ENV{'REMOTE_HOST'} || 'LOCAL';
open(LOG, ">>$LOG") || die("Failed to open log file: $LOG\n");
printf LOG ("[%s] %s %s \"%s\"\n",$time,$host,$addr,$message);
}
#------------------------------------------------------------------------
sub check {
my($pname,$OKstring,$OKpexp,$error) = @_;
$OK{$pname} = $OKstring;
printf ("%-15s\n%-15s %-20s\n","${pname}=${$pname}",$OKpexp,$OKstring)
if (defined $DEBUG);
(${$pname} =~ /^$OKpexp$/) ||
&error("\"${pname}\" (${$pname}) $ERR $OKstring\n");
return 1;
}
#------------------------------------------------------------------------
## Beware of passing unchecked headers from the web!!!
sub send_mail {
my($to,$from,$subject,$message,@headers) = @_;
open (MAIL, "|-") || exec($SENDMAIL);
print MAIL <<EOM;
Date: $DATE
To: $to
From: $from
Subject: $subject
@headers
$message
EOM
close MAIL;
}
#------------------------------------------------------------------------
sub get_info {
## Take over form params
for ($query->param){
(s/M_//) ? (@{$_} = $query->param("${&}$_"))
: (${$_} = $query->param($_));
}
}
#------------------------------------------------------------------------
sub error {
my $message = @_[0];
print <<EOM;
Content-type: text/html
<HTML>
<TITLE>Processing Error:</TITLE>
<BODY>
<H1>Processing Error:</H1>
<HR>
<PRE>$message</PRE>
<HR>
Please make the necessary corrections.
</BODY>
</HTML>
EOM
(defined $LOG) && &log_it($message);
die($message);
}
#------------------------------------------------------------------------
sub redirect {
my $url = @_[0];
print <<EOM;
Status: 302 Moved Temporarily
Method: GET
URI: <$url>
Location: $url
Content-type: text/html
EOM
}
#------------------------------------------------------------------------
sub validpw {
my $salt = (getpwnam($_[0]))[1];
return (crypt($_[1], $salt) eq $salt) ? 1: 0 ;
}
#################################################################
### End Subroutines
#################################################################
### MAIN (Here's where the untested stuff begins)
use CGI;
$query = new CGI;
get_info;
### Just to check one paramter that might come a little close to a shell
check('PARAM','\w{2,20}','2-20 alphanumerics allowed.');
## To check for form parameters that aren't going to be passed to a shell,
## but are at least required:
$REQ = '.{1,1000}';
$REQtxt = 'This field is required (up to 1000 characters).';
for ('PARAM2','PARAM2','PARAM3','PARAM4','PARAM5','PARAM6') {
check($_,$REQ,$REQtxt);
}
print $query->header;
print $query->start_html;
$PARAM2
$PARAM3
## You get the idea ...
EOM
print $query->end_html;
__END__
Rob Muhlestein
CGI Guy
Teleport Internet Services
http://www.teleport.com/