http://www.perlmonks.org?node_id=38548

CGI security is a frequent topic of dicussion on this site so I thought I'd raise another issue that illustrates a variety of problems that people face when trying to write secure scripts. The following example is rather simple, but helps to illustrate the dangers involved (my information came from this Phrack article. tilly originally supplied me with the link).

I'm posting this here in part so that I can archive this and link to it from my online CGI course. I figured it would be a handy way to get people to use Perlmonks more :)

Consider the following code:

#!C:/perl/bin/perl.exe -wT use strict; use CGI; # Do not run this script on a server connected to the 'Net # It is supplied as a bad example my $cgi = CGI->new(); my $file = $cgi->param( 'file' ); # Bad taint checking! # This is, amongst other things, a deliberately incomplete list # of shell metacharacters my $data = $1 if $file =~ m#([^./\\`$"'&]+\.?[^./\\`$"'&]+)$#; $data .= '.dat'; my $userInfo; open FILE, "<$data" or die "Cannot open $data: $!\n"; { local $/; $userInfo = <FILE>; } close FILE; print $cgi->header, $cgi->start_html, $cgi->pre( $userInfo ), $cgi->end_html;
Hmm.... what's wrong with it? The author (me) uses warnings, strict, and taint checking. Users need to be able to get at the contents of .dat files on the server, but filenames can be very unpredictable. This script allows them to view those files from any browser. Therefore, we need to (gasp!) let user data near the shell.

The author, however, knows this is a security hole, so he conventiently supplies a list of shell metacharacters which he doesn't want. Further, he appends a ".dat" extension to guarantee that the user can only view files with said extension. Looks pretty secure to me!

I deliberately left the list of shell metacharacters incomplete so Monks wouldn't be tempted to use this technique.

So what's the problem? Perl, as many of you know, is written in C. C recognizes the null byte (ASCII zero) as the end of a string. Perl does not. If Perl tries to make a system call (such as open FILE, $somefile) with an embedded null byte in the data, the data is passed to the C underbelly, which happily ignores any information from the null byte on. So how does that affect our script?

I named the script insecure.cgi and ran it locally through a browser. I used the following URL:

http://localhost/cgi-bin/insecure.cgi?file=insecure.cgi%00loser!
See that embedded %00? Hmm... it's right after the name of my script. It passes our taint checking and Perl thinks it's trying to open insecure.cgi%00loser!.dat. However, the C underbelly tries to open insecure.cgi, and does! Then, the rest of the script happily dumps its source code to your browser.

This illustrates a couple of points:

Specifying what you won't allow forces you to go over everything that you missed and ensure that there are no security problems. How many would have guessed that ASCII zero would be a risk?

This node brought to you by the letter C

Cheers,
Ovid

Join the Perlmonks Setiathome Group or just go the the link and check out our stats.

Replies are listed 'Best First'.
RE: CGI Security and the null byte problem
by extremely (Priest) on Oct 26, 2000 at 10:39 UTC
    You left out a simple example or two on how to specify what you will allow. So, Allow me =)
    my $file = $cgi->param( 'file' ); my $data; # # One way # $file =~ s#[^A-Z0-9a-z.]+##g; #strip down to alphanum and period # can change the meaning of what people post. $file =~ m#(.*)#; #evil evil evil if you haven't striped. $data = $1 || ""; #not really necessary to have alternate, nice tho. # # Alternate way # $file =~ m#([A-Z0-9a-z.]+)#; #grab first good chunk. # can potentially ignore a lot of data and boggle the user $data = $1 | ""; #you really want this now, match can fail. # # My "best" way # die "Eeeek! Evil data sent to the 'file' parameter!\n" if ($file =~ m#[^A-Z0-9a-z.]#); #now use the first method above to detaint anyway...

    --
    $you = new YOU;
    honk() if $you->love(perl)

RE: CGI Security and the null byte problem
by mdillon (Priest) on Oct 26, 2000 at 11:00 UTC

    speaking of null byte problems, i was once bit while trying to do join("\0", array('a', 'b', 'c')) in some PHP code.

    it doesn't work!

    the result is (in hex):

    61 62 63 00 00

    one could say, perhaps, that there is a moral to all this...

    use perl;

    for those to whom it isn't apparent, the correct result (which are easily produced by the equivalent Perl, join "\0", qw(a b c)) is:

    61 00 62 00 63