Not a bash bug
paste.lisp.org
paste.lisp.org
This undocumented implementation detail is also a limitation on the use of regular environment variables, and should be documented. When reading documentation about a mechanism, I expect that special magical strings which change behaviour of the mechanism are clearly documented. If such documentation had existed, someone might have noticed it and guarded against it.
> Assumedly programs like apache filter out environment variables properly. But unfortunately, in the validation of input data, they fails to validate correctly input data because they don't expect that data starting with "() {" will be interpreted by their bash child processes. If there's a bug, it's not in bash, but in apache and the other internet facing programs that call bash without properly validating and controlling the data they pass to bash.
It isn't easy to validate and control data against an unknown magical feature in one of many possible shells.
> But on the other hand, it is free software and not difficult to check the source to see as the nose in the middle of the face, what is done. When reusing a component with missing specifications and lacking documentation, checking the source of the implementation should be standard procedure, but it has clearly not been done by Apache or DHCP developers.
I think the shell is specified in POSIX/SUS. Checking the source of all possible open-source shells would be a huge job. I don't know how they should check source code of the closed-source shells. I don't blame them for using the environment variables according to available documentation.
Edit: typo
Usually the amount of toxic environment variables are considered to be finite; PATH, LD_PRELOAD, etc., etc. If the name of any executable on the PATH is dangerous, than the number of toxic environment variables is infinite -- are we to scan the entire PATH for each environment variable to make sure it isn't dangerous? What if the CGI script updates PATH?
There is no way to solve this problem with sanity checks. I've yet to peek at the source, but I'm told this feature is vital to implementing things like backtick operators. I think it is too dangerous however, and I don't want shellshock to become a class of bug rather than an instance of toxic environment variables. We're going to have to rip this feature out and re-implement large portions of functionality.
The author is right that this is a product of bash being written in a more trusting time. This is not the first nor the last time the 1970s security models will come back to bite us.
edit: forgot reference:
[1] http://seclists.org/oss-sec/2014/q3/741
edited to add:
Also, Apache does have a mechanism to filter out toxic environment variables; headers are added as HTTP_HEADER_NAME, because its generally the names of environment variables that allows them to be dangerous and not their content. Executing code as a result of parsing the value of an environment variable with no special meaning is a vulnerability.
If you can set arbitrary environment variables, you're pwned and have always been pwned. You can set all manner of interesting things, including LD_PRELOAD, to control the execution environment and potentially execute arbitrary code.
EDIT: Putting random data in an environment variable where you pick the name should always be secure, though, which is an assumption that most of *nix makes.
Would it be possible to wrap|tag input to bash so that only when a program|script sets the env variable with string that's typed as "executable" does bash even think of exec-ing it. I guess that removes some of the hack-ability and would need major rewriting of bash.
I'm a layman trying to do CS ... what could possibly go wrong!
SOMEVAR="`cat some_binary_file`"()SOMEVAR@%=...
You will get a parse error. There is little more than [a-zA-Z0-9_] you can use in identifiers (except bash adds a few more, grrrr). You can probably pull it of with /usr/bin/env though.
Even before the redhat patch you would need something to set echo=() { ... but how will an attacker do that when they can only set something like HTTP_USER_AGENT=() { ... ? See how overriding a builtin is not and never was a vulnerability?
I say this without animosity to bash devs. I think some blame can be shared. But putting it all on people you expect to understand under-documented behavior and "implementation details" in every possible version of every possible flavor of /bin/sh is madness.
By letting everyone on the Internet set shall variables Apache and whatever DHCPd (ISC?) did something they could have known would have bad consequences whether this feature/bug existed or not.
The only data Apache needs to control is Apaches.
You don't with execvp or execlp either.
> or wildcards or argument parsing.
IMO this is of dubious value from, say, a C program. Why "parse" the args? Just generate a list...
I think the real bug is that all this stuff calls out to a shell at all. Sure, it's convenient, but it's basically eval().
You should never call out to anything by passing untrusted user input directly. Any information that came from the outside must be explicitly passed as data through proper serialization mechanisms.
For instance, you don't piece your SQL queries by concatenating strings. You use an abstraction layers, in which you code the query structure and you pass user input as data. There is this extra step of saying "this is data, not code" that strips the external input from executability.
(for the same reasons, if your templating engine is just concatenating strings and not building the page out of trees, you're doing it wrong, but it's a topic for another day)
It's a problem you get when you believe in "the Unix way" a bit too much. Yes, everything is text, but no, not everything has the same semantics.
So if I call a CGI script with parameters foo=bar, what data should apache pass to the handler, if not something along the lines of the string "foo=bar"? When I pass the header "User-Agent: baz" and the handler asks for the user-agent, what should it be told if not "baz"?
Environment variables are data, not code. When apache executes a cgi script, whether it's C or perl or shell, it makes the user input available as data in defined locations.
There's a bug in bash which causes some of that data to be executed, but there's no way to protect against that class of bug.
This isn't a case of "you should have protected against sql injection attacks". It's a case of: there is a bug in your sql server, such that the query "select from Users where username='rm -rf /'" will execute "rm -rf /"*.
Similarly, if a Ruby or Perl script decides to call out to bash with untrusted user data, it's their mistake to trust bash with it, not bash's mistake that it wasn't designed for that use case.
It's perfectly possible to protect against this attack: don't call a generic program with untrusted user data.
How do I know what other programs are designed for such a task? What's a "generic program"? At this day and age, it is expected that pretty much all software ought to be designed with security in mind (not that it always is). Because any piece of "generic software" (or just software) is otherwise going to be exploited. Especially on platform where double-clicking a file is the expected way to open it.
More importantly, the point we are making is that we're not expecting bash to "handle" anything. It gets some data. It's not supposed to do anything with it on its own. Period.
Perl actually is when used in taint mode. http://perldoc.perl.org/perlsec.html
$ x='() { :;}; echo vulnerable' perl -t -le'$ENV{PATH}="/bin";print `:;date`'
vulnerable
Sat Sep 27 10:51:12 PDT 2014In most cases, the shell is just used to find programs in the PATH when a C programmer uses system(). And for that case, which is probably 99% of the time when /bin/sh is being invoked, it would make perfect sense to implement this with something that exhibits less attack surface.
Taking the "dhcp-exploit" as an example (set a DHCP option on your server to "(){...}; exploit;"), I think it's less clear: Implementing the functionality of updating configuration files according to the DHCP options sent is a prefecty reasonable place to use a script written in sh/ksh/bash! It's easy to implement by any sysadmin, works very reliably with a little care, and performance-wise it's not critical at all.
And regardless of the language you implement it: There's some place where user-input has to be sanitized, but up to now, it was considered common knowledge that arbitrary data in an environment variable is safe as long as the variables' name adheres to some convention (prefix them all with PROGNAME_...). And bash doesn't respect this convention by looking at variable CONTENT, even though I'm pretty sure that it was already established when the bash-project started... (see, for example, handling of "special" variables like LD_xxx in suid programs or the dynamic linker)
I said it in another thread but this is almost always a mistake. The execve family is much less ambiguous about what gets passed to the program. Using it avoids this type of bug by not putting the shell where it doesn't need to be.
> And for that case, which is probably 99% of the time when /bin/sh is being invoked, it would make perfect sense to implement this with something that exhibits less attack surface.
To me, use of system() is very indicative that you need to find another C programmer. There are few other answers to complete the phrase "when a C programmer uses system()".
The fact is: system() and all it's relatives (popen comes immediately to mind, there are doubtlessly 100 others) have been used, will be used, by 'incompetent' programmers[+] and as long as no other method is as widely established (and: even taught in introductory textbooks), we better provide a workaround that closes most of the holes.
[+] or just programmers weighting the merits of having a parser supporting variable and home-directory expansion, curtesy of /bin/sh -c right built in, which is completely adequate for many tasks. And yes, I know the limitations of it, and would not use it myself most of the time.
subprocess.call(["date"])
Or if bash is not your default shell:
subprocess.call(["bash", "-c", "date"])
Also another function to worry about is popen().
> In an interview Thursday, Mr. Fox, the Bash inventor, joked that his first reaction to the Shellshock discovery was, “Aha, my plan worked.”
http://mobile.nytimes.com/2014/09/26/technology/security-exp...
* In Unix, shell scripts and shell subprocesses are everywhere, and are supposed to be everywhere.
* Environment variables are passed across subprocesses by default, you need to explicitly filter the environment to prevent that.
Therefore, if you write a shell, the reasonable assumption is that it's going to be integrated into pretty much all "systems"/programs running on a Unix box with this shell, and environment variables will travel from everywhere to everywhere.
Of course the feature isn't a "bash security bug"; it's just one of those endless poorly documented, weird special cases which together make up what we know as "Unix".
And the answer to anyone shooting themselves in the foot, whether it's one person or a billion, is "you should have read the documentation" - and now, apparently, "you should have read the source".
It's a good thing this kind of "design philosophy" is absent outside the field of computer programming. Generally the vendor should be the extremely diligent party, and the user is assumed to be reasonably naive - even if the user is a professional (think power tools, etc.) It is only programmers, for some reason, who think that it's perfectly fine to add whatever features they want to their code without much worrying about consequences, and leaving the worrying to the users.
* A file system should store your bytes, though nothing prevents a file system written in C from executing, say, logged HTTP requests as commands.
* A CGI script should sanitize form data, though nothing prevents a PHP script from blithely shoving unsanitized data into SQL queries.
* And a shell should pass environment variables to subprocesses, though nothing prevents it from interpreting variable values (or names, or a combination of names, values and the time of day) as commands.
As I said - I don't think it's a security bug in bash, just another one of endless misfeatures. It's about as crazy to interpret environment variable values as code because they have a special-cased form as it would be to interpret, say, file names as code, or certain byte sequences passed to the write() system call as code, etc.
Of course some people would pipe to shell in their .forward file and eventually get pwnt, but it was a freshman mistake, and the damage was isolated.
Once you reach the point of executing a shell with an euid other than your own, it's not the shell's job to sanity check your actions.
The web has changed the execution model thoroughly. And people now do lazy things based on their loose understanding of flexible execution models.
This is neither a bug in bash, nor a bug in Apache, etc. It's an integration bug between two complex systems that were designed with zero-to-poor knowledge of each other.
cat downloaded_file.txt |\
while read inputline
do
#some processing
#call another shell program
done
In this case, bash would still be vulnerable even without the Internet involved -- just processing a data file you got from Bob in Accounting.As to the article you linked to, I recall that it mentions that the feature in question is actually from the early 90s when it might well have become a security bug... though I still think it's beside the point.
I'm sure rsh sounded fine when it was written, but....
It's absurd to think that a transport layer should be responsible for "validating" all possible contexts in which the data it transports could be used. How is apache supposed to know the difference between using a magic string and simply mentioning it? How is apache supposed to know what magic strings apply to all possible subprocesses of sh that inherit environment variables? A program that receives user data has to be responsible for validating that data, and it's not productive to characterize the lack of validation as a "feature" just because it can be used to provide functionality.
This kind of misguided thinking leads to practices like mod_security refusing to allow comments that contain the magic strings "1=1" because they might be trying to inject SQL into something.
In other words, if Apache/dhclient wants to put things into environment variables, it absolutely should make sure to do this properly and indeed to take into account all possible contexts.
But the problem emerges from trusting 100% of inputs, not just the inputs that are intended to be trusted.
I am also pissed that they patched the parser, the feature is still in there when nobody uses it and it has already proven to be a security vuln once.
Input you type at the terminal is not the same as an environment variable; hackers set environment variables much more often than legitimate users.
And/or change the way these definitions are handled - I get that it's tantalizingly simple to just pass this through their usual parser since then you get the function definition parsing for free, but at the very least this parsing of the environment variables shouldn't go through a code path that even potentially executes anything.
KSH and ZSH will load functions from files they find via $FPATH.
And they can't take into account all possible contexts, because that would involve reading the mind of all possible future users of the interfaces they provide, to make sure none of them decides to call out to programs that treats previously inert data as code.
For what Apache knows, that CGI it executes could treat the presence of the letter "x" in any environment variable as "start a nuclear war".
While it may make sense for Apache to sanitize the data against specific known, common problems, until this week this was not a specific known problem. It might have been if the Apache team audited the code of every plausible piece of code that people might use to interpret CGI scripts, but that's not a realistic scenario.
Random person on the internet shouldn't be able to set shell variables. Not 'x', not anything else.
Your advice is really: don't use shell scripts any where near untrusted data. Tracking which data is trusted and which isn't across different processes in different languages across different systems is not a trivial task. So really the advice is: don't use shell scripts. That's sound advice, but not something Apache can or should enforce.
That isn't sufficient. Your argument means you can't use shell for anything. If you can't trust it not to execute the contents of a variable, then it should never be used other than on isolated systems where the data it processes comes from completely controlled sources. Using the shell becomes the equivalent of using the gets() function in C.
That means a complete redesign of all linux distros, for a start. You are going to have to some better justification for throwing away an entire operating system ecosystem just to preserve a behaviour in bash that basically nobody uses.
But otherwise, in the context of the internet of today, yes, we'd need a completely different operating system (perhaps something based on capabilities). Unix indeed doesn't seem to be good enough for a safe internet.
If we could say that there are N bugs, and with openssh and this bash bug, we only need to correct N-2 bugs and we'll be fine, then perhaps we could keep unix (and similar systems).
But it just looks like it's more a systemic problem (indeed not specifically an apache bug, or a X or Y bug, but bugs emerging from the interaction between two or more components in the unix ecosystem), therefore if we don't change the fundamentals, we cannot exclude that we will keep introducing and discovering this kind of bugs again and again.
Shell != Environmental variables.
As someone else said, traditionally only the names of shell variables have mattered, not the content. Apache exports most of its envars named HTTP_* as an attempt to somewhat de-fang them.
That some crusty CGI app spawned a bash process which then chose to do something outrageous with the content of HTTP_COOKIE really isn't Apache's fault. Seriously.
Apache transmitting data created by random people to CGI processes using environment variables is most definitely Apaches fault. It was a dumb idea in the 90s and it's a dumb idea now.
I think environment variables were used either due to naivity or an ultimately mistaken concept of simplicity.
Sunning up the entire thread: Apache should have used a socket, and should have known they needed to.
Well, there's nothing inherently dangerous about setting environment values. Yes, some have special meanings in special contexts but so could data piped over a socket.
So your argument boils down to: Apache should have seen the special treatment of data in this context, and used another context instead.
That's fine, but it doesn't address the real problem, which is that the shell was not designed to be executed on behalf of other users. There's no spec to say that data received from the "other" channel will not be interpreted or used unexpectedly.
Until you have that guarantee, you're just rearranging the problem space. There's no systemic improvement.
Yes, the behaviour of all Unix shells and their poor separation of data from instructions is well known in the 90s.
> There's no spec to say that data received from the "other" channel will not be interpreted or used unexpectedly.
That is true. However something specifically designed as a communications channel, such as a socket or FIFO, is generally better suited than something designed as a shell.
But the shell shouldn't evaluate data as instructions. This is the bug!
Apache could have used a socket and I could write a buggy endpoint which evaluates data read from that socket as instructions. Tada, same problem, same bug.
Shells are insecure. You could replace them with something compatible but insecure, but they wouldn't be a POSIX shell anymore. Hence the well known SetUID blocking on shell scripts, hence not letting random people on the internet set shell variables.
You can talk all you want about unrelated gotchas in shells & security, and that's missing the point.
Absolutely nothing about the CGI attack vector is unique to Apache. It could occur with any webserver that supports CGI which, up until nginx, was pretty much all of them.
Apache could refuse to provide a mod_cgi, in which case it would never have gained the position it did, and some other server with support for CGI would have.
If that's true, then the spec has been obviously, fundamentally broken for it's entire existence.
That shellshock exploits will still be possible in this configuration is once again, not Apache's problem.
Saying that Apache (and other apps) passing data from random people on the internet to a known insecure environment like a shell, that was known to be insecure in the 90s, is 'not Apache's problem' doesn't actually absolve Apache of responsibility for its own programming.
2) I'd wager that the people who came up with CGI initially had expected that the process apache spawns would be anything but a shell.
3) Non-shell CGI processes happily deal with all manner of binary, back-ticks, dollar signs etc. in their HTTP_ envars all day and have done so for nearly 20 years. It's not a huge leap in logic then that these envars should be considered capable of holding arbitrary data without exploding.
4) You make it sound like nobody has considered environment variables a problem before, but sanitizing the environment before spawning a process from the CGI was already a well-established best-practice way before this bug came along. That process looks like - whitelist of envar names, remove all others; check PATH/LD_LIBRARY_PATH/etc sanity; command string parameters use specialized token substitution (eg. "echo %{integer}%" where some bespoke code throws an error if %{integer}% is interpolated to anything other than an integer), etc.
5) Despite the insanity of running CGI stuff which I would generally agree isn't a great idea, I'm pretty sure I'm allowed to be surprised that the mere content of an environment variable, whose job it is is to contain arbitrary data to be passed on to the CGI app should cause things to explode.
Generally agreed though, I'd love to see the shell actually store data from instructions too, but it would break a lot of things.
If you don't want random person on the internet to be able to set shell variables, either don't enable mod_cgi, or don't use CGI's written in a shell script.
Some people write cgi in bash.
Yeah.
There is nothing inherently insecure in that: The environment is just a bunch of strings.
Whether or not it is insecure depends 100% on the CGI that gets executed. Which programs that is 100% down to the person configuring the website.
Apache does not even have a theoretical way to ensure those applications does not do anything stupid with the data no matter the method used to pass it.
You have the simple solution if you are concerned about environment variable passing of not trusting mod_cgi. Most people have not used it for years anyway, because of the performance impact.
if you run a .php file and you have mod_php or the webserver has understanding of the concept of PHP and calls the binary directly, all nice and good
if you have a something.randomext or something without an extension at all, then... whelp
luckily there is a program that is dedicated to working out how to run random executable files, the shell (and it uses the shell-bang, or shbang for short), so in that situation, the call will be done though the shell
With this exploit, bash is not behaving as advertised. If you pass it an environment variable with a certain value, it executes code. There is no way to sanitize your input to completely protect against this class of bug.
The most consistent moral I'm deriving here, is that shells are for executing arbitrary commands in a flexible environment, so don't even touch them if that's not what you want. This applies to Apache, CGI wrappers, any other kind of web development.
At first glance, I think that's your problem.
Fundamentally, though, you can't always point to "it's this one component that is broken." There is the classic example that was used to criticize Authenticode, which was Microsoft's way of dealing with mobile code back in the 1990's. It said "all the code is signed, so if something breaks, you know who to sue."
Well, imagine two programs:
1. One of them formats your hard drive, and on installation puts itself into your Quick Start list.
2. One of them runs every single program in your Quick Start list at start up.
Which piece of code is responsible for formatting your hard drive?
And yes, I agree it's difficult to assign blame.
I guess you have to strip down the environment to only the things you need to know. That's unfortunate, because the nice thing about the environment is that it's inherited, so the user can put in configuration that's needed 3 or 4 steps down the line from where it originated.
Is that really what you are advocating here? Don't ever use shell scripts with untrusted data? That has been my philosophy for a long time, but up till now that position has been viewed as extreme by most people I've shared it with.
[1] there are still exploitable bash parser issues to be revealed http://seclists.org/oss-sec/2014/q3/777
We shouldn't have to give up popen() just because bash was designed insecurely; we should fix or replace bash.
Sorry if I'm picking on you too much, but I noticed this and thought of an even better counterexample. By this logic, it's Apache's job to prevent SQL injections. It should know that a single quote, in one possible context, can terminate an SQL string, popping the SQL parser into a state that allows for arbitrary statement injection.
It also needs to know about any other database language that could ever possibly be invented, now and for eternity, because we can't know how long any given version of Apache will remain in use.
Apache? No. mod_cgi? Yeah, because that's its fucking job because that's how CGI works http://tools.ietf.org/html/rfc3875
'meta-variable'
A named parameter which carries information from the server to the
script. It is not necessarily a variable in the operating
system's environment, although that is the most common
implementation.
I suppose the authors of bash know that most of the implementations of CGI do this so they should be preventing bugs like this. Thanks for your clarification. I still maintain that this sounds like a very insecure mechanism for passing information from Apache to your application.http://tools.ietf.org/html/rfc3875#section-7.2
For UNIX compatible operating systems, the following are defined:
Meta-Variables
Meta-variables are passed to the script in identically named
environment variables. These are accessed by the C library
routine getenv() or variable environ.
This is how CGI works. This is how CGI has always worked. NCSA HTTPd was built on and for Unix, and was the origin of what we now call CGI. This is the interface to which CGI-compliant scripts and webservers have always adhered.> I suppose the authors of bash know that most of the implementations of CGI do this
1) I don't know why you would suppose the authors of Bash would know how CGI works. Nothing about writing a shell implies knowledge of the intricacies of web technologies.
Certainly many of the people on HN so eagerly looking to blame either Bash or Apache don't seem to know how it works. If they did, they might have realized what quesera so astutely observed earlier today[0]:
"This is neither a bug in bash, nor a bug in Apache, etc. It's an integration bug between two complex systems that were designed with zero-to-poor knowledge of each other."
2) I don't know when this particular Bash feature originated, but Bash as a whole pre-dates CGI by about five years.
> I still maintain that this sounds like a very insecure mechanism
I don't know of anyone who has looked at CGI and thought the use of environment variables was a good idea in the face of the 21st century's security landscape.
This mess was more than two decades in the making. It is the collision of two entirely unrelated courses plotted by two entirely unrelated parties, neither of whom could have been expected to know what the other was doing, and neither of whom could have foreseen how yet more unrelated parties would (in some cases unknowingly!) combine their works into a time bomb.
Which makes OP's comparison to Ariane 5, a spacecraft with an infamous bug that could be described as an "integration bug" (between older and newer parts of the software), even funnier. Hopefully the development teams of a rocket were a bit more in-sync than those of Apache, bash, distribution maintainers (some of whom have made /bin/sh link to bash), and CGI programmers.
Don’t worry.
> By this logic, it's Apache's job to prevent SQL injections.
Curiously, I’d call this an example for my position, not yours: It is not the job of the SQL server to prevent SQL injections, quite the contrary, it is the job of the calling application to ensure that the things it tells the SQL server to do are actually safe. Similarly, it is the job of Apache to ensure that the things it tells its child processes to do are safe.
…
Actually, you’re probably right – if I’m guessing correctly that all the "CGI" standard does is to say "These user-supplied values go into these environment variables", then it is not the job of Apache to sanitise them. It merely means that Bash is an unsuitable choice for an environment with untrusted environment variables, i.e. as a CGI script or even for use as /bin/sh if you cannot trust environment variables.
It gets a bit messier with dhclient, because that explicitly calls a Bash script and hence definitely should know about the issues Bash might have with environment variables.
http://tools.ietf.org/html/rfc3875#section-3.4
http://tools.ietf.org/html/rfc3875#section-4.1.18
http://tools.ietf.org/html/rfc3875#section-7.2
http://tools.ietf.org/html/rfc3875#section-9
HTTP header values have some limited formatting requirements[0], but are otherwise arbitrary. There is no general way for the server to know which values are safe and which are not. That would require the server to know how the script would respond to any particular input. If that were the case, the script would be superfluous, and the server could simply respond to the client with the result it already knows the script would return!
True. But perfectly valid SQL shouldn't cause the SQL server to be exploitable. And the application has no idea which SQL statements are exploitable.
> Similarly, it is the job of Apache to ensure that the things it tells its child processes to do are safe.
True. But the unsaid assumption is "safe from the point of Apache". Not "safe from the point of some unknown exploit".
So it's reasonable for an application to sanitize what it passes to the shell, by dealing with known and documented issues. e.g. Don't pass "$FOO" in a string to system() and expect it to be treated as the literal "$FOO" string.
It's unreasonable to expect that the application know everything about the attack surface of everything it uses. Maybe there's a bug in an SQL library which causes the application to crash when a SELECT statement contains the text "crashme". This text is valid SQL, and it is entirely unreasonable to expect that the application "sanitize" such text.
In the same way, environment variables are strings. It's a bit surprising to discover that bash will execute code contained in random environment variables.
The problem, as you note, is that in order to correctly implement the CGI standard, Apache must pass the problematic data. Even if it took care to see "I'm about to pass this to bash, which might do something stupid with it, so I'll fix that", it can't know that a non-bash CGI executable is going to pass it to bash somewhere down the line.
I agree with your other conclusions. People will definitely be re-evaluating shells, CGI, and environment variables for at least a few months or so while this is fresh.
That sometimes people opted to use bash as the script interpreter and/or that people sometimes shell out to bash from within other environment without sanitizing the environment they pass along, is not Apache's fault.
This latter is really something we should do irrespective of where the input is coming from. Even if the content comes from trusted sources, passing the environment unchanged to sub-processes means we risk all kinds of unintended consequences if an application developers decides to spawn a sub-process that exec's into some process that makes decisions on various variables.
It's a bit like letting our applications call functions with random junk in the argument list.
I don't think this history is correct.
At the time, most systems used sh as the default non-interactive shell. The other shells such as csh, ksh and bash were considered to add interactive niceties, but programmers and sysadmins expected scripts to use sh.
One of the reasons sh doesn't have a lot of wiz-bang features is that it needs to stay compatible and portable across all the different Unixes so that all the scripts, past and present, will work. And sysadmins liked this conservative approach.
It wasn't until the rise of Linux, and the aliasing of /bin/sh to point to bash, that sysadmins started becoming comfortable with running bash as the default non-interactive shell. And that was only because bash promised to be fully compatible with sh. And bash was compatible but it also added new scripting features. But most developers and sysadmins rejected using these new features because you would lose portability to sh systems if you did so. That is until now, with the dominance of Linux and portability becoming less of a concern.
So really this bug is the fault of bash's embrace-and-extend strategy.
This has always been and always will be a hard problem. Consider this quote which I found in The Shellcoder's Handbook:
"Wherever terms have a shifting meaning, independent sets of considerations are liable to become complicated together, and reasonings and results are frequently falsified." -- Ada Lovelace
We've known about this since literally the beginning; we'll be cursing ourselves over it until the very end. Vulnerabilities are going nowhere.
This has nothing to do with parsing text. The problem here is that Apache et all send untrusted data to a process that treats it as code. It wouldn't matter if HTTP was a binary protocol and if bash read a well defined bytecode instead. I mean, look at shellcodes.
I assume you're talking about Apache - but Apache had no way of validating the data. The protocol just said "this is a blob from the client", which any binary protocol for the task must be able to handle. Apache had no business validating it, anymore than it should validate any other content - how should it know what makes it valid?
Bash, on the other hand, just received that blob and treated it as an executable. It wouldn't matter if the protocol between the server and bash was binary, since it was a valid value as far as the protocol was concerned.
The problem here is the hidden channel between Apache and bash, which never actually talk directly to each other (it's through the CGI binary) but still pass data. It has nothing to do with text protocols.
You seem to be relying on some assumption that you have about human psychology for your security gain. Somehow people would never do that with a binary protocol, and text protocols make them more comfortable and trusting. At least they can read text protocols directly; binary protocols involve me trusting a bunch of middleware I'm using to read them, too, or writing my own (always great for security.)
The matter is that Apache and the protocols (HTTP and environment vars) are just being used as a tunnel between the attacker and bash. They can't pass functions via another channel because they don't know what functions are. All they know is they're passing blobs of data - which any protocol would do, binary or not.
Bash happens to recognize a text value as functions, but it could just as easily recognize the magic value of an ELF binary and execute that, or any other binary format used to encode functions.
TCP is a binary protocol, how does it distinguish between executable and plain text formats? Answer: it doesn't, because TCP doesn't know or care about that, that's left to the layers above to handle.
Likewise, environment variables don't know or care about "functions", that's a concept that doesn't enter into the protocol, since it's not a shell specific protocol. All it transmits are keys and values, which are generic blobs of data. That bash uses the protocol to transmit code mixed up with data is no more the protocol's fault than the fact that TCP was used to transmit those same functions on HTTP requests.
"The Unix Way" means throwing away all semantic data - passing plain strings with no context, which are then parsed and re-parsed in an completely ad-hoc manner, usually with regexp-based shotgun parsers.
Note how SQL injections, or XSS attacks are prevented - people stopped stiching strings together and started generating proper instructions through code. User input is sanitized and driven through process that converts it from untrusted string to trusted data structure. Typing SQL queries in a semantic-aware system looks almost the same as stiching strings (thanks to SQL being flat), but now you can't possibly SQL-inject yourself.
So in general: stick to text formats or not, but whatever you do, never glue data structures using tools that work on data medium layer, that are not aware of the structure and meaning of data they are operating on. E.g. never glue strings to build SQL queries or HTML code.
Disclaimer: I may be crabby from a busy week of mitigation. Still, I don't hold any particular ill will for the original bug. But a bug it is.
And: The magic value is undocumented.
And: Any instance in the entire tree of calls will do this.
Some of this is strictly on bash. But some of it also has to do with the environment feature of Unix, which is basically a god object with all the power and temptation ("look what we could get for free if we let it be a function!") that entails.
Bullshit, this may well be perfectly valid data and web servers are not in the business of shielding shells against their own misfeatures.
And if they were where would they stop? It would require that webservers do arbitrary context-sensitive data analysis of everything doing through in case this is a malformed JSON string triggering a bug in GSON while that is data injected unescaped into an SQL variable and the other one's too big for an underlying C buffer.
You can only end up with a webserver refusing to do anything, because some idiotic application somewhere may misuse or misunderstand anything it lets through.
> It's not so obvious that a shell should have to deal with malicious environment variables, due to the reasons outlined in the original post.
Which I don't care for, my point is that the webserver can not wipe the ass of every bug or misfeature implemented by the shit put behind it. It's just not possible.
[0] e.g. "magic" object keys or keysets, most libraries expose ways to hook into object deserialisation to do exactly that but they could do it by default as well, and I'm sure there are some which do
[1] which is exactly what bash does here
All the CGI caller can and should do is forward correct data as defined by RFC 3875, the rest is not its job.
> sanitizing environment variables before calling bash.
The CGI caller may not even be calling bash, then what? Should it remove anything which looks like valid PHP code because it's calling a PHP CGI? Oh but now the PHP CGI uses system() which creates a subshell which is still holed, and we end back with: if it becomes the CGI caller's job to cleanup data which could be misinterpreted by application code, the only thing it can do is stop working entirely.
Now if you want a mod_bash_is_retarded prefilter feel free to implement one, but it most definitely is not mod_cgi's job to fix that crap, mod_cgi's job is to correctly implement RFC 3875, and the number of times bash is mentioned in RFC 3875 is 0.
He's saying if Apache passes a request to mod_cgi, which spawns "someapp", it is not Apache, but "someapp" that should sanitize the environment before it calls bash.
(and of course if the developer/admin has chosen to write their script to be run by bash, that's their mistake)
edit:
"A brief description: Ruby on Rails makes extensive use of a serialization format called YAML, most commonly (you might think) for reading e.g. configuration files on the server. The core insight behind the recent spat of Rails issues is that YAML deserialization is extraordinarily dangerous. YAML has a documented and 'obvious' feature to deserialize into arbitrary objects. Security researchers became aware in late December that just initializing well-crafted objects from well-chosen classes can cause arbitrary code to be executed, without requiring any particular cooperation from the victim application."
So what was 'obvious' then is the opposite of what is 'obvious' now.
What if I have a printer called "() { :;}; echo lol", so I manually export PRINTER="() { :;}; echo lol" (see http://www.tldp.org/HOWTO/Printing-Usage-HOWTO-4.html)
I don't think this was the intended behavior even 25 years ago. :)
In 1989 or so, I was doing network services and server support for
[Case Western Reserve] University (CWRU), and was not satisfied with
the shells I had available for that work. [1]
I had the priviledge of hearing Chet speak about his experiences
maintaining Bash.[2] From my perspective he has done a really great job
over the years making software that many people love to use and abuse.So while this is a really bad network security situation for the internet at large I think it is dubious to hold Chet or even Bash at particular fault. Rather, we are all at fault. We have been writing software that just shells out to Bash or sh or z-shell for years because it is convient. We could have easily have written our subprocess code in better ways but it was easy to use shells and we used them, even when we didn't really understand them.
[1] http://www.computerworld.com.au/article/222764/a-z_programmi...
[2] The venue was Link State a student run conference here at CWRU
Firstly, the "security concern" is definitely a bug.
It is related to bugs in a feature; nobody is saying that that feature itself is a bug, though many are saying it's a "misfeature". So the idea that the feature itself is a bug isn't something that requires opposition.
The idea that it's the Apache people's fault somehow, because they didn't inspect the implementation of something that they rely on, is wrong.
> When reusing a component with missing specifications and lacking documentation, checking the source of the implementation should be standard procedure, but it has clearly not been done by Apache or DHCP developers.
Firstly, the CGI mechanism doesn't rely on Bash; it relies on the passage of environment variables.
Secondly, for the shell language, there is a specification: POSIX. Someone calling a shell implementation should be able to rely on the interface contract. Nowhere in POSIX is it documented that code from environment variables is to be executed by the shell.
Apache can run on systems that don't have Bash. A shell other than bash can be used for running a CGI shell script. You don't get the source code, necessarily; how can the Apache developers inspect the source code of a proprietary shell on a proprietary Unix?
Even if the Apache or DHCP developers were to (insanely) take responsibility for this flaw, the workarounds in their code would be Bash-specific hacks: basically they would have to parse anything that goes into an environment variable and validate that it doesn't have the syntax which exploits the Bash issue. That would clearly indicate that it's a Bash problem.
What also indicates that it's a Bash problem is the way it is being handled: fixes have been issued against Bash, not against other programs. The fire is where the smoke is, generally, and that's where you pour the water.
Lastly, please don't post diatribes to a code paste hosting service; it is not your soap box. Thanks!
On the other hand, after thinking about it, there are a number of reasons why I decided that this is at best a misfeature of Bash.
It is incredibly undocumented. I've been a Unix guy for over 25 years, and I've been using Bash for most of that time. (Sorry, David Korn.) I've used Bash a lot. But I've never heard of this thing.
It violates some ill-defined, personal, un-thought-about assumptions about environment variables. An environment variable with executable code? That's as terrifying as LD_LIBRARY_PATH, and that is very well known. One reason I've probably missed this feature is that it is something I would never consider using.
In my opinion, it's almost impossible to secure this on the parent process' side. Sure, the parent can look for magic Bash strings, but.... This isn't just Apache, it's potentially every other network accessible program that calls a shell, and that is a very common thing to do in Unix.
Finally, consider some of the special behavior of execlp and execvp:
"If the header of a file isn't recognized (the attempted execve(2) failed with the error ENOEXEC), these functions will execute the shell (/bin/sh) with the path of the file as its first argument. (If this attempt fails, no further searching is done.)"
You could end up starting a shell without knowing.
Take the internet today in general as a huge example of that issue; it was never developed with anonymity or privacy or security in mind and here ware are, horrified of even just the tip of the iceberg that was revealed through Manning and Snowden. If the early researchers and engineers had built the early technologies with fundamental, most basic human considerations in mind we might not be looking down the barrel of a dystopian dawn.
So my question is whether anyone is aware of a method, procedures, techniques, etc. to plan for such a paradox?
Specifically, the literal string "Security issues are not discussed in this memo.' is found 568 times in the 3000 first RFCs. (6 times in the RFCs from 3000 to 5887).
When searchers were inventing the internet, they just put aside security considerations. In a way, security was enforced at the boundary, by universities controlling their teachers and students who could use it.
When the internet becomes a public network, where anybody can send packets on it, of course security considerations become a priority, but the protocols weren't designed for security. Like IPv6, we'd need to design a new set of protocols for this public internet, taking into account security considerations as a priority.
But given the speed with which IPv6 is adopted, you can guess with what readiness a new set of secure protocols will be adopted (you'd also have to be able to trust them, that no NSA or other backdoor is hardwired in those new protocols).
In short, this is not a simple situation and there is no simple solution.
If this is considered an "implementation detail", then I'm even more convinced that the whole idea of hiding implementation details, and thus these surprising (mis-)features, is fundamentally flawed.
But on the other hand, it is free software and not difficult to check the source to see as the nose in the middle of the face, what is done.
There is absolutely nowhere in the official bash manual that mentions the special behaviour of environment variables with values starting with '() {', not even in the "differences from POSIX/Bourne Shell" list, so the natural expectation is that any sequence of bytes not containing the 0 byte (since this is a C interface) can be put into the contents of an environment variable. On the other hand it does have an extensive list of reserved variable names which have special meaning.
To quote the POSIX spec on environment variables (emphasis mine) - http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_... :
The values that the environment variables may be assigned are NOT RESTRICTED except that they are considered to end with a null byte and the total space used to store the environment and the arguments to the process is limited to {ARG_MAX} bytes.
Thus the reasonable expectation is that Bash behaves according to the POSIX spec; it's even mentioned in http://www.gnu.org/software/bash/manual/bash.html#Major-Diff... that "Bash is POSIX-conformant."
The fact that the function used to evaluate imported function definitions was named parse_and_execute(), and is basically the same function that executes regular commands at the prompt, was what stood out to me the most upon hearing of this behaviour although in retrospect, it wasn't all that surprising.
This is a bug that, by any other name, would be just as disturbing.
The part where you can define functions by setting environment variables is a feature. It would be far better if the variable names needed a prefix (e.g. BASH_FN_foo='() {...}')., but even without that change, fixing this bug still preserves the case that allowing arbitrary data into the values of a whitelist of environment variables is safe.
In general, allowing modifications to a subset of the environment (and by this I mean the entire system environment, not environment variables) needs to be safe. Allowing programs to upload arbitrary data to /tmp/uploads is safe, allowing programs to upload to e.g. ~/.profile is clearly not safe.
In PJBs world it is not possible to set any environment variable to any value without reading the source code of every program that might possibly be called by any of your children. This is clearly not tenable.
Oh fuck, this feature is a wonderfull feature in a controled environment for passing objects/code over a simple octet stream. With safe computer paradigm.
We could have done RPC easily with xinetd + and shell scripts. With PAM we could have even be able to use kerberos to control the security...
I could have done lots of things... I still can ...
Bash I still hate you for not documenting this, and more YOU Advanced Bash Scripting guide for being so awesome and missing that. ABS you failed me. http://www.tldp.org/LDP/abs/html/functions.html
Whether that be Apache (if it's passing data or commands directly to shell that contain data from the external environment) or a CGI script it is calling, whatever happens to be interacting directly with the shell should be sanitizing its inputs.
The linked article's premise requires that the bug/feature be present when Bash was first written, not be the result of more recent changes, a a time when the risks were obvious. I can't show this, but I doubt it.
What made me look for it, was a suspicion that it was some backdoor added more recently. But apparently no, it's a feature that always existed in bash (granted, an ill-advised and ill-implemented undocumented feature, but still).
Even absent that, contrary to the article, I think I'd still call this a bug in bash - but the crash (presuming I'm recalling correctly) makes this position absurd instead of just (IMO) incorrect.
I think the maintainer is right. Bash was never marketed as a secure sandbox and anyone who uses it as such is taking that risk on themselves.
This wasn't the problem. The problem wasn't with scripts getting untrusted input. The problem was with bash getting untrusted input -- input that isn't supposed to be evalled in the first place.
I know patching `bash` is most important (and I have). But it would give some extra comfort.
It's just my imagination, but I keep picturing someone who did point out the possible security risks but was then dismissed as being too paranoid.
bash might not be intended to be that way, but it is assumed by developers to be that way, which means it has to be that way (or it can never be used as system shell).