[CentOS] bash - safely pass untrusted strings?

Tue Feb 26 21:42:34 UTC 2008
Les Mikesell <lesmikesell at gmail.com>

Benjamin Smith wrote:
> On Tuesday 26 February 2008, Les Mikesell wrote:
>>> WHY THE @!#! NOT?!?!?
>> The shell is 'supposed' to be run by a user that is allowed to run any 
>> command he wants, and permission/trust issues are handled by the 
>> login/authentication process that happens before you get to the shell. 
>> If you give the shell a bad command under your own account, it's not 
>> supposed to second guess what you wanted.
> 
> I'm not asking for this. I'm only asking for the option to be able to trust 
> that a parameter is... a parameter. EG: 

You can, but you have to know how many times shells will parse it.  One 
layer of quoting is removed each time.  Things inside single quotes are 
literal, double quotes still do variable expansion.

> 
> file: script1.sh 
> #! /bin/bash
> script2.sh $1 
> exit 0; 
> 
> file: script2.sh 
> #! /bin/bash 
> echo $1; 
> 
> $ script1.sh "this\ parameter"; 
> 
> I get output of "this"! script2 gets two parameters! I want a way for 1 
> parameter to STAY 1 parameter upon request, so that script2.sh would 
> output "this parameter", like 

One layer of quotes for each time it is parsed...  The first is on the 
initial command line, so if you want to hold it together, put double 
quotes around "$1".

> 
> file:script1.sh 
> #! /bin/bash
> PassToShell2=escapethis $1; 
> script2.sh $PassToShell; 
> exit 0; 
> 
>>> Bash is used, extensively in many cases, to deal with untrusted data.
>> Why?
> 
> How about an installer script? How about a magical script copied from TLDP to 
> rename all files in pwd? 

These things run with the permissions of the user running them.  Why 
should they be concerned about that person giving them untrusted 
embedded commands that they could just as easily run directly?

>>> This can 
>>> include random file names in user home directories, parameters on various 
>>> scripts, etc. It's highly sensitive to being passed characters that have, 
>>> over the past NN years, resulted in quite a number of security holes and 
>>> problems. 
>> If it hurts, don't do it.  Build your own argument list and exec 
>> programs directly if you want to avoid shell command line parsing.
> 
> So, I'm supposed to know the contents of a user's home directory? And code for 
> these in advance? 

Code so you don't let the shell parse their names.  It doesn't have to 
if you just want to hand them to some other program.

>>> Yet there exists NO MECHANISM for simply ensuring that a given argument is 
> an 
>>> escaped string? 
>> What does that mean?  If you can define it you can make it happen, but 
>> who knows what characters at what depth of quoting will have some 
>> special meaning?
> 
> Can I define it? Thought I did that already:
> http://us.php.net/manual/en/function.escapeshellarg.php

Can you pass that via ssh to some other system(s) and have i/o 
redirection or variable expansions happen in the right places?

> Or its perl equivalent: 
> http://search.cpan.org/~gaas/URI-1.35/URI/Escape.pm
> 
> See how I'd like to see it in implementation in above example, "passToShell2"

What's the point?  If you are in the shell already it's too late.  If 
you are in some other program and don't want shell metacharacter 
processing to happen, don't feed it to the shell in the first place.

>>> How many "homebrew" ISP or hosting administration scripts could be 
> compromised 
>>> by simply putting a file in your home directory called ";rm -rf /" ? 
>> Probably none that are still in business.
> 
> Google "bash howto" for lots of vulnerable and problematic examples. Here's a 
> beaut that fails if you have a file called "-a" in the pwd, see "File 
> re-namer". It's a renamer that doesn't, if the file contains any spaces, 
> dashes, etc. 

There are any number of ways to do things wrong.  I don't need to look 
up more of them.

> 
> http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-12.html#ss12.1
> 
> Here's what I get: 
> 
> mv: invalid option -- a
> Try `mv --help' for more information.

Most programs accept -- as an option to end option parsing.  But if you 
don't want this effect, don't start your filenames with a -.

> 
> Or with a file with a space: 
> echo "blah" > "d"; 
> echo "blah" > "d foo"; 
> 
> The TLDP's example doesn't move file "d foo". I get: 
> mv: cannot stat `d': No such file or directory
> mv: cannot stat `foo': No such file or directory

If you don't want the shell to split on spaces you can tell it not to. 
Or you can quote filenames correctly.  Or not put spaces in filenames.

> So I ask again: This doesn't strike you as fundamentally borkeD? The emperor 
> wears no clothes! 

No, it works the way I'd want it to work most of the time.  And for the 
exceptions, you use quotes in the right places the right number of times.

>>> This doesn't strike you as fundamentally borkeD?
>> No, if you stop bad things from happening, you'll also stop good things.
> 
> Yes. But you don't have to stop the good things.

You don't like the fact that the shell does things like splitting on 
white space?


> I think the *OPTION* of 
> saying "parameter 1 is STILL parameter 1" is a good thing. If you want to 
> leave things be, so be it. See my above example. 

You can't do that without knowing at what point you want it to stop 
being an opaque blob.

>> The problem is that you aren't using the shell as intended.  If you run 
>> it under your own user id, it does exactly what you tell it to do and 
>> there is no element of trust involved.
> 
> The problem, as I see it, is that the shell provides access variables without 
> any means of preserving them as variables across calls and incantations.

It does.  You just have to know when it should and when it shouldn't

>> Errr what???  Php has about the worst security history of any program 
>> around.
> 
> Thanks for confusing the issue with a red herring. Or should I ignore the 
> buggy and probably vulnerable TLDP example above? Maybe a google search 
> for "bash escape vulnerability" might illuminate the issue I speak of?

No, you should just learn to use quotes. Or bypass shell processing.

>>> This just blows my mind....
>> What would you like your computer to prevent you from doing to yourself?
> 
> I hate to belabor it: give me the OPTION to trust that I can keep a single 
> parameter as a single parameter across incantations and calls. If I'm looping 
> thru a listing files, I should be able to trust that my $FILENAME variable 
> contains the name of... a file!

A file can be be named pretty much anything, so what does that mean? All 
you can do is make sure it is quoted as many times as you let the shell 
parse it.

> If I want to pass parameter 1 of my script to 
> another script, that other script should be ABLE get my parameter 1 as... 
> parameter 1! 

OK - quote it.

> A simple call that lets me take a string and get a passable parameter would 
> suffice. 

But in shell, such a call would parse whatever you give it - and the 
parsing would follow the same rules as every other parsing operation...

> You want to apply scripts that break with a space in a file name? Great. Have 
> at it. I don't want to detract from your ability to make an alphabet soup of 
> your directories with borken examples from TLDP, and all the goodness that 
> implies to you. 
> 
> But I think admins the world over would like it if a simple, one-liner could 
> be added to ensure that a single parameter remains a single parameter!
> 
> Am I being unclear?

You want the shell to not be the shell, I think.  Everything the shell 
does, it does the same way, which is why we like it in the first place. 
    You can't tell it to do something without parsing the command and 
its arguments.

-- 
   Les Mikesell
     lesmikesell at gmail.com