Shellshock and Perl

Recently the tech media have been foaming at the mouth over a serious Bash bug called Shellshock. The media hype machine was in full-swing, replete with the absurd doomsday-like predictions that are rolled out every time a significant security vulnerability is found (remember heartbleed the “ultimate web nightmare”?). Whilst it’s wise to ignore the hype, don’t ignore the issue; Shellshock is a serious risk that allows remote code injection and execution using Bash environment variables. This is also important for Perl as Perl has several touchpoints with the system shell, from the built-in functions exec and system to the %ENV global variable.

Is system “x” affected?

If the platform is a Unix-based operating system and Bash is the default terminal, it might be at risk. Redhat Linux, CentOS and Fedora, openSUSE, arch Linux as well as Mac OSX are vulnerable out of the box. A surprising number of platforms are not; freeBSD uses tsch, and modern versions of Debian and Ubuntu use dash by default.

Every version of Bash through 4.3 is vulnerable to Shellshock. To find out your Bash version, fire up the terminal and enter this command to print the version:

$ echo $BASH_VERSION
4.2.47(1)-release

Seeing as my version of Bash is below 4.3, my system is possible vulnerable to Shellshock.

How does Shellshock work?

Shellshock exploits a flaw in how Bash parses environment variables; Bash allows functions to be stored in environment variables, but the issue is Bash will execute any code placed after the function in the environment variable value. Let’s craft an example:

$ export SHELLSHOCK="() { ignore; };echo danger"

This code creates a new environment variable called SHELLSHOCK (it’s customary to have environment variable names in uppercase). The value of the new variable is an anonymous function which does nothing: () { ignore; }; followed by: echo danger and it’s the latter portion of this code which is the risk. Every time Bash processes its environment variables, that code will be executed. For example if I run that statement and then type:

$ bash -c "echo Hello, World"
danger
Hello, World

You can see that the word danger was printed, indicating my code embedded in the SHELLSHOCK variable was executed automatically by Bash. In the case of echo danger it’s harmless, but an attacker could craft a malicious payload that caused irreparable harm, such as identity theft, data destruction or hardware damage.

In order for the Shellshock exploit to work, the attacker would need to achieve two things. First deliver an environment variable containing malicious code to the target host. Second, get the target host to start a new Bash process. The obvious target candidate for this are web servers hosting CGI scripts. CGI works by passing the request parameters as environment variables (such as the user agent name), if the target CGI script starts a new Bash process, the attack will work. You might be wondering why a script would start a new Bash process, which leads me on to how all of this relates to Perl in the first place.

Perl shock

The first thing to say is that Perl has nothing to do with Shellshock, but there are a number of places where Perl may invoke the system shell, and it’s these cases to be wary of. On Unix based systems Perl uses the shell binary located at /bin/sh, which is usually a symlink to the default shell binary (such as Bash). This means if Bash is the default shell on your system, when Perl calls out to /bin/sh a new Bash process will start, and the environment variables will be processed, thus Perl could be a trigger for invoking a Shellshock attack.

The Perl built-in functions exec and system will invoke a new shell process when used. You can also use backticks to invoke a system command. Other Perl functions may invoke the shell, for example open can be used to run system commands.

Let’s see an example of Perl triggering Shellshock by invoking the shell via Perl:

$ perl -e 'system "echo test"'
test

Hmm what happened here? The command ran fine but “danger” was not printed - Shellshock failed. It turns out that Perl doesn’t always invoke the shell using: /bin/sh -c. Instead to be more efficient, Perl will usually call execvp. According to perldoc, only when the system command contains metacharacters, will Perl invoke the shell directly. Let’s test that:

$ perl -e 'system "echo test >> test.log"'
danger

Aha, this worked! We used the metacharacters >> to redirect the output of echo into a log file, and Perl invoked the shell directly.

The best defense is a great offense

Instead of worrying about whether our system calls contain metacharacters, we can go one better and delete the SHELLSHOCK environment variable before executing any system command. Perl stores the environment variables in %ENV, so I’ll start by delete the variable from there:

$ perl -e 'delete $ENV{SHELLSHOCK};system "echo test >> shellshock.log"'

In this one liner, I’m front-running the risky systemcommand with a delete of the SHELLSHOCK environment variable. I can see this thwarted Shellshock as “danger” was not printed out. Of course in this test environment I know the name of the dangerous environment variable, but usually I won’t, so to find it, you’d have to iterate through the %ENV hash and delete (or substitute) any suspicious variable. This one liner prints risky environment variables by using a regex to identify any environment variable that contains code after a function declaration:

$ perl -E 'for (keys %ENV) { say if $ENV{$_} =~ /};.+/ }'
SHELLSHOCK

As you can see, it correctly identified the SHELLSHOCK environment variable and printed it to command line. From here it’s a trivial step to delete the variable instead of printing it:

$ perl -e 'for (keys %ENV) { delete $ENV{$_} if $ENV{$_} =~ /};./ }'

This is just a proof-of-concept and may not handle all maliciously crafted Shell environment variables, but with more research, a robust regex could be deployed that completely nullified Shellshock.

Conclusion

To recap, a successful Shellshock attack would need to pass an environment variable containing malicious code to a CGI script on a web server (like Apache), hosted on a vulnerable system, and the CGI script would have to invoke the Shell. For Perl CGI scripts, the system invocation would need to include metacharacters. This seems like a tall order, not yet understood by everyone; like the security blogger who mistakenly labelled a cPanel CGI script as vulnerable. Although CGI was popular back in the day, all the modern Perl web frameworks use FastCGI and are immune to Shellshock. Modern web servers do not enable CGI by default and some like nginx do not even ship with CGI capability.

The safest way to handle Shellshock on a vulnerable system is to patch Bash to the latest version. Although I’ve shown it’s possible to thwart the attack using Perl, there may be other unanticipated attack vectors that remain open.

Correction: removed erroneous description of $SHELL as it is the current user’s default login shell, not the default shell. Removed reference to .bashrc as Bash will only process .bashrc during interactive shell startup. 2014-09-27

Cover image Ebola virus particles © NIAID

Tags

David Farrell

David is the founder and editor of PerlTricks.com. An organizer of the New York Perl Meetup, he works for ZipRecruiter as a software developer.

Browse their articles