Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re: Hacking CGI - security and exploitation

by crazyinsomniac (Prior)
on Jun 24, 2002 at 21:47 UTC ( [id://176957]=note: print w/replies, xml ) Need Help??


in reply to Hacking CGI - security and exploitation

Hmm, is it me, or is this a whole lot of hooey, and I quote:

Perl Length Limits

I might as well mention this here just because I haven't seen it in any other papers. I have only seen this problem only a few times but things like this do exist and 99% of the perl coders out there never even think about it. This is when perl limits filename sizes, variable sizes, and limits other such things which can effect how things in a script work. With this example you can effect things such as -e check, open, unlink, and other file handling functions operate.

The problem is simple, take this code as an example, although long should be simple enough for newbies to understand:

#check for bad characters if($FORM{'path'} =~ m/\0|\r|\n/ig){ die "illegal characters"; } #check for .htaccess file in /home/user/accounts/$FORM{path} $htaccess = "/home/user/accounts/$FORM{user}/.htaccess"; if(-e $htaccess){ #read .htaccess open(HTACCESS, "<", $htaccess) or die "could not open .htaccess file"; @lines = <HTACCESS>; close(HTACCESS); #get username and password ($correctuser,$correctpassword) = split(/:/,$lines[0]); #check if they are right, give access if($FORM{'user'} eq $correctuser && $FORM{'pass'} eq $correctpassword){ print "access granted"; access(); } else{ print "access denied"; } } #if the .htaccess does not exist then create a new account. else{ #makes the directory #error unless the directory already exists #if it exists than the script thinks it's just missing .htaccess mkdir($FORM{'user'},0755) or die "error accessing user directory" unless (-d $FORM{user}); #create .htaccess file and print username:password #this should be encrypted but it is just an example. $accessfile = $FORM{'user'} . "/.htaccess"; open(USERACCESS, ">", $useraccess) or die "could not create user file"; print USERACCESS "$username:$password"; close(USERACCESS); }

So what does this code do? It will check if /home/user/accounts$FORM{'path'}/.htaccess exists, if it does it will check the submitted username and password against the real one. If it doesn't exist then it will create a new user directory and a .htaccess in it with the submitted username and password. This looks secure from all the previous types of attacks, but because perl limits filename sizes to around 2050 bytes (atleast that is what it is on my box) it can be exploited.

So lets say someone has the account with the username of admin. Their home directory would be /home/user/accounts/admin/ and their username:password would be in /home/user/accounts/admin/.htaccess usually this would protect people from accessing this directory. But if an attacker submits ././././././././././././[another 2000 bytes of this[./././admin as $FORM{'user'} there is trouble. The attacker will need to make ././././[etc#&093;./././admin/.htaccess a valid length so that the .htaccess file is created when the script does open(USERACCESS, ">" $useraccess) but will fail the if(-e $htaccess) when another 20 bytes are added from the '/home/user/accounts/'.

There are other possible ways to exploit scripts based on how perl sets size limits, this is a very tricky thing to find and even harder to remind yourself of these limits while coding. Best stratigy is to limit sizes of all input to a reasonable length (few hundred characters) and be very aggressive when checking if files/values exist. I would also suggest using sysopen instead of open, take this for example:

    sysopen(FILE, $file, O_WRONLY | O_CREAT);

No need to worry about perl's length restrictions as sysopen will not overwrite a file. Also helps those silly race conditions that old perl versions have.. not really a CGI problem though. You can easily check what your perl limits filenames to by doing something like this:

    linux:~ # perl -e 'while(1){$n++;unless(-e "./" x $n){ die "perl sets limit at " . (--$n);}}'

This will tell you the limit on the number of characters perl allows before it cannot open, unlink, check for existance, or any other simular file handling functions. I'd be interested in hearing if anyones is way off from 2050 (b0iler@hotmail.com) or if this is a constant value. Also if anyone else can think of a way to abuse other perl limits, I have found a few.. but they seem too high to exploit or there is no situation where they would cause a problem.

 
______crazyinsomniac_____________________________
Of all the things I've lost, I miss my mind the most.
perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"

Edited: ~Mon Jun 24 22:21:50 2002 (GMT),
by Footpad: Adjusted formatting to address complaints from Mozilla users mentioned via CB.

Replies are listed 'Best First'.
Re: Re: Hacking CGI - security and exploitation
by cjf (Parson) on Jun 25, 2002 at 02:41 UTC

    Well, he at least appears to be trying to use imaginary variables. If you change some of them around, you can eventually get a script that works (if you can call it that), you'll also have to run it from the 'accounts' directory, I didn't fix that:

    $FORM{'user'} = "cjf"; $FORM{'pass'} = "1234"; # why was the following line there? # if($FORM{'path'} =~ m/\0|\r|\n/ig){ die "illegal characters"; } #check for .htaccess file in /home/user/accounts/$FORM{path} $htaccess = "/home/cjf/accounts/$FORM{user}/.htaccess"; if (-e $htaccess){ open(HTACCESS, "<", $htaccess) or die "could not open .htaccess f +ile"; # added chomp chomp(@lines = <HTACCESS>); close(HTACCESS); ($correctuser,$correctpassword) = split(/:/,$lines[0]); if ($FORM{'user'} eq $correctuser && $FORM{'pass'} eq $correctpass +word){ print "access granted"; access(); } else { print "access denied"; } } else { mkdir($FORM{'user'},0755) or die "error accessing user directory" +unless (-d $FORM{user}); $accessfile = $FORM{'user'} . "/.htaccess"; # changed $useraccess to $accessfile # changed $username to $FORM{'user'} # changed $password to $FORM{'pass'} open(USERACCESS, ">", $accessfile) or die "could not create user f +ile"; print USERACCESS "$FORM{'user'}:$FORM{'pass'}"; close(USERACCESS); }

    Now I'm still not sure what he's saying about filename/variable limits in Perl and how they could result in a vulnerability. It certainly doesn't sound accurate. Can someone clarify this?

      Any time the system does something the programmer doesn't expect, you have potential problems.

      In this case the programmer is using apparently equivalent file names - one for testing existence of a file and one for creating it. They are not equivalent though because the filesystem limit means that one name is invalid and the other is a file that already exists. So the code thought it was creating a new file to represent a new permission - but instead was replacing an existing access file.

      So yes. The code presented makes the mistake described. But it would be a non-issue without a whole series of supporting mistakes - first and foremost of which is testing a different filename than you are creating!

      Oh, to answer your other question? The "following line" was there because disagreements between Perl and system calls on the meaning of a null byte can cause all sorts of fun. Also shell scripts being confused by returns can cause other fun and games. Those characters were therefore known to be dangerous, and therefore were eliminated.

        Thanks for the reply. As for the following:

        # why was the following line there? # if($FORM{'path'} =~ m/\0|\r|\n/ig){ die "illegal characters"; }

        I was wondering why he bothers removing those characters from a variable that is never used. Perhaps he meant $FORM{'user'} in the $htaccess assignment to be $FORM{'path'}?

Re: Re: Hacking CGI - security and exploitation
by Anonymous Monk on Jun 25, 2002 at 02:28 UTC
    Those limits are very real, but have nothing to do with Perl. They are a function of your OS and filesystem.

    Of course not checking for directory traversal (../../..) is an even worse mistake. But bonus points for three arg open...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://176957]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2024-04-19 11:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found