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:
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.#!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;
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:
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.http://localhost/cgi-bin/insecure.cgi?file=insecure.cgi%00loser!
This illustrates a couple of points:
- Don't let user data near the shell.
- If you must let user data near the shell, use taint checking and specify what you will allow!!!
- Don't store data files in the same directory as your CGI scripts
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.