Nik has asked for the wisdom of the Perl Monks concerning the following question:

http://nikos.no-ip.org/cgi-bin/index.pl/?select=../../cgi-bin/index.pl +%00
Is there a way to avoid the above? Icant understand this becaus i sue the following code:
my $passage = param('select') || "&#913;&#961;&#967;&#953;&#954;&#942; + &#931;&#949;&#955;&#943;&#948;&#945;!"; Encode::from_to($passage, "utf8", "ISO-8859-7") if param(); if ( param('select') ) { open(FILE, "<../data/text/$passage.txt") or die $!; local $/; $data = <FILE>; close(FILE);
There is a .txt suffic after the variable $passage so the null byte injection shouldn't work.
Why does it work?

Replies are listed 'Best First'.
Re: How to avoid Null Byte Injection
by chargrill (Parson) on Oct 07, 2006 at 23:28 UTC

    A little googling showed me the following (taken from here: http://artofhacking.com/files/phrack/phrack55/P55-07.TXT):

    Quoting "Rain Forest Puppy":

    You see, Perl allows NUL characters in its variables as data. Unlike C, NUL is not a string delimiter. So, "root" != "root\0". But, the underlying system/kernel calls are programmed in C, which DOES recognize NUL as a delimiter. So the end result? Perl passes "rfp\0.db", but the underlying libs stop processing when they hit the first (our) NUL.

    What if we had a script that allowed trusted junior admins to change passwords on anyone's account EXCEPT root? The code could be:

    $user=$ARGV[1] # user the jr admin wants to change if ($user ne "root"){ # do whatever needs to be done for this user } (**NOTE: this is here in WAY simplistic form & theory just to illustrate the point)

    So, if the jr. admin tries 'root' as the name, it won't do anything. But, if the jr. admin passes 'root\0', Perl will succeed the test, and execute the block. Now, when systems calls are piped out (unless it's all done in Perl, which is possible, but not likely), that NUL will be effectively dropped, and actions will be happening on root's record.

    While this is not necessarily a security problem in itself, it is definitely an interesting feature to watch for. I've seen many CGIs that tack on a ".html" to some user-submitted form data for the resulting page. I.e.

    page.cgi?page=1

    winds up showing me 1.html. Semi-secure, because it adds ".html" page, so you'd think, at worst, it'd only show HTML pages. Well, if we send it

    page.cgi?page=page.cgi%00 (%00 == '\0' escaped)

    then the script will wind up feeding us a copy of its own source! Even a check with Perl's '-e' will fail:

    $file="/etc/passwd\0.txt.whatever.we.want"; die("hahaha! Caught you!) if($file eq "/etc/passwd"); if (-e $file){ open (FILE, ">$file");}

    This will succeed (if there is, in fact, an /etc/passwd), and open it for writing.

    Solution? Simple! Remove NULs. In Perl, it's as simple as

    $insecure_data=~s/\0//g;

    Note: don't escape them with the rest of the shell metacharacters. Completely remove them.

    Please note:The indented text is not my own, I just thought it answered Nik's question and formatted it for our forum.



    --chargrill
    s**lil*; $*=join'',sort split q**; s;.*;grr; &&s+(.(.)).+$2$1+; $; = qq-$_-;s,.*,ahc,;$,.=chop for split q,,,reverse;print for($,,$;,$*,$/)
Re: How to avoid Null Byte Injection?
by sgifford (Prior) on Oct 08, 2006 at 06:02 UTC
    First, consider turning taint mode on, which will stop your program if it is about to use unfiltered data. Doing that will force you to decide what is and isn't an allowed character before you can do anything dangerous with a user-supplied string.

    Once you've done that, make sure the pattern you use to untaint doesn't allow null characters. For example:

    my $filename; if ($param('select') =~ /^(\w+)$/) { $filename = $1; } else { die "Illegal filename!\n"; }
      Umm, thanks but i think it would be better if i would be checking param('select') against the valid text file names(*.txt) inside my /data/text/ folder like Joost suggested.
      After all its only one of those values that i really want.
        You're right, I didn't realize you had a small, fixed list of filenames that were allowed.

        I would still recommend turning on taint mode, though. It stops you from accidentally introducing security problems at places in your program you may not have thought of.

Re: How to avoid Null Byte Injection
by Joost (Canon) on Oct 07, 2006 at 23:21 UTC
      I tried what you said but evenif the user select a valid filename drom the drop down menu his request still gets rejected.
      Where did i go wrong in the following code?
      my @files = <../data/text/*.txt>; my @display_files = map /([^\/]+)\.txt/, @files; Encode::from_to($_, "ISO-8859-7", "utf8") for @display_files; print br; print start_form( action=>'index.pl' ); print h1( {class=>'lime'}, "&#917;&#960;&#941;&#955;&#949;&#958;&# +949; &#964;&#959; &#954;&#949;&#943;&#956;&#949;&#957;&#959; &#960;&# +959;&#965; &#963;&#949; &#949;&#957;&#948;&#953;&#945;&#966;&#941;&#9 +61;&#949;&#953; => ", popup_menu( -name=>'select', -values=> +\@display_files ), submit('&#917;&#956;&#966;&#940;&#957; +&#953;&#963;&#951;')); print end_form; my $passage = param('select') || "&#913;&#961;&#967;&#953;&#954;&#942; + &#931;&#949;&#955;&#943;&#948;&#945;!"; Encode::from_to($passage, "utf8", "ISO-8859-7") if param(); if ( param('select') ) { unless ( $passage =~ /^[a-zA-Z&#945;-&#969;&#913;-&#937;0-9]+$/ ) { print br() x 2; print h1( {class=>'big'}, "*Backward Directory Traversal* hack wi +ll NOT help you here, Mighty Lamer!" ); exit; } open(FILE, "<../data/text/$passage.txt") or die $!; local $/; $data = <FILE>; close(FILE); Encode::from_to($passage, "ISO-8859-7", "utf8"); $select = $dbh->prepare( "UPDATE guestlog SET passage=?, date=?, c +ounter=counter+1 WHERE host=?" ); $select->execute( $passage, $date, $host ); } else