Re: Can System() of Perl be bypassed?

From: Brian Hatch (secprogat_private)
Date: Wed Jan 22 2003 - 13:58:01 PST

  • Next message: Ilya Martynov: "Re: Can System() of Perl be bypassed?"

    > In my PERL code,I am using user's input as command line argument for the 
    > program being executed by System().
    > Can user run command of his choice by giving malicious input?
    > Is PERL's -T (Taint mode) the solution for this?
    
    Using Taint mode is a good thing.  However you can still easily
    untaint inappropriately:
    
    	$user_filename = $q->param('filename');
    	$user_filename =~ /^ (.*) $/x;
    
    	$filename = $1;
    
    	system "/bin/ls $filename";
    
    Sure, the filename submitted by the user was untainted and put into
    $filename.  But since the untainting didn't do anything to actually
    check the input (it was just extracted via $1, no checks or munging
    whatsoever) a user could easily supply "/etc/passwd; rm -rf /"
    as a filename and system will happily process it.
    
    Lesson 1:  do real untainting.  Make sure the filename looks
    exactly like you want it:
    
    	$user_filename =~ /^ ([a-zA-Z]+) $/x;
    
    This would allow files that are 100% alphabetic, for example.
    No shell metacharacters, no parent (..) directories, or any
    directory (/) stuff at all.
    
    
    Naturally, what a 'valid' value is depends on your needs.
    
    Lesson 2: say what is valid, don't decide what isn't valid.
    
    The best untainting is when you explicitly say what is all right.
    If you were trying to extract a suitable filename (anywhere
    on the filesystem, including the use of .. and /'s) you'd want
    something like this:
    	
    	/^ ( [\w./]+ ) $/x;
    
    which details exactly what you'd consider valid.  If instead
    you try to get rid of 'bad' data, such as shell expansion
    characters:
    
    	/^ ( [^;\$&]+ ) $/x;
    
    Then you're liable to miss some.  For example  "*" is missing
    from the above list.
    
    
    Lesson 3: always use the array form of system in perl.
    
    System will pass your command to /bin/sh if it thinks there
    are shell metacharacters to expand.  If you're using user
    input, don't do this.  (Heck, never do this - it's poor
    form anyway.)  Instead, use the array version, which will
    do a fork/exec of your command explicitly, and never run
    the shell.  Thus you'd want to have
    
    	system( "/bin/ls", $filename);
    
    If somehow your untainting failed, and the user was allowed
    to supply "/etc/passwd; rm -rf /" for the filename, you would
    end up running
    
    	ls  "/etc/passwd; rm -rf /"
    
    And, assuming you don't have a file named "/etc/passwd; rm -rf /"
    ls will simply complain that no such file exists.
    
    
    
    Short answer:  unless you program with lots of paranoia, any
    programming language can be abused, perl included.
    
    
    --
    Brian Hatch                  "I've got as much chance of
       Systems and                doing that as seeing a
       Security Engineer          Vorlon doing strip-tease."
    http://www.ifokr.org/bri/
    
    Every message PGP signed
    
    
    



    This archive was generated by hypermail 2b30 : Wed Jan 22 2003 - 14:27:32 PST