Mitigating Bash ShellShock

Following the frenzy of patch releases in reaction to the CVE-2014-6271 Bash Vulnerability (ShellShock), several blogs and articles were published detailing the vulnerability, but there has been less discussion on the steps one can take to mitigate the threat.

 

This blog seeks to provide actionable information and recommendations for readers to proactively defend against ShellShock.
Overall, the vulnerability in and of itself does not allow remote execution, but rather the implementation of a vulnerable bash shell as a part of a network based service. We will first review why a vulnerable local shell is dangerous for remote execution. Second, we will identify specific steps to mitigate the risk within your network and illustrate a repeatable example of a web page that provides the avenue for remote execution. Realistically, there are a lot of machines out there that need to be patched, and patching them all will take some time. Therefore, the information provided in these steps is aimed at helping improve the efficiency of this process and limiting the amount of exposure and damage that ShellShock may otherwise have on your network. At the end of this blog, the Recommendations for Mitigation

 

section highlights tools and techniques that administrators can use to protect their networks.

The Vulnerability

CVE-2014-6271 vulnerability is described by NIST as “Bash through 4.3 processes trailing strings after function definitions in the values of environment variables, which allows remote attackers to execute arbitrary code via a crafted environment”. This means any strings trailing a bash function will be executed, when the function and strings are stored in an environmental variable, and then passed to a spawned shell via the exporting of that variable. The “crafted environment” NIST mentions, refers to how the arbitrary code is executed and the requirement that variables storing the function be parsed by bash. When a new bash shell is spawned, it parses all environmental variables provided or passed to it. In our example below, the export command ensures that the variable declared will be passed to any subsequent environments called by bash. The example below shows how CVE-2014-6271 can be executed within a bash shell environment.

 

In the above example, the “echo” command is executed upon the spawning of the new bash shell. By exporting a variable that contains a function “() { :; };” with a trailing command “echo …”, the desired arbitrary execution is achieved.

The Environment

The configuration we’re using is an OSX 10.9.5 with PHP 5.4.30 running the web service. However your configuration and environment will vary. Note that this part of our set-up does not contain the vulnerability. The “-S” option binds our local loopback address on TCP port 8080 and the “-t” defines the local directory as the root directory of the webserver. Our web server is hosting two files detailed in the sections below:
  • index.php
  • bashcgi.sh

The Index

The web server looks for and executes “index.php” by default. Notice in this case that our page is executing the intended bash script, which will run an unpatched (vulnerable) version of bash. The vulnerable script, titled “bashcgi.sh”, is given the HTTP GET request variable named “mycmd” as the first argument upon script execution. If you were to open a web browser to “http://127.0.0.1/index.php?mycmd=asdf”; the command line interpretation of this would be:$ ./bashcgi.sh asdf

The Bash Script

As you’ll notice in the script code, the first argument (“$1”) does not even come anywhere near traditional execution. Variables stored in scripting are not typically used for execution unless directly referenced by the command interpreter. In this instance the first argument passed to the script is exported as the variable named “vardec” for variable declaration. In the case of our previous example with the command “$./bashcgi.sh asdf”, the executed code within the script would look like: “export vardec=asdf”. Thus any subsequent environments would be able to make reference to the “vardec” variable. To take advantage of this vulnerability from a local command line standpoint you can execute the script with the following argument:

$ ./bashcgi.sh "() { :; }; touch my_touched_file"

The script will execute and run the arbitrary code. A new file will be created in the same directory called “my_touched_file”.

Remote Execution

By leveraging a specially crafted HTTP GET request, we can use the variable passed to the script to take advantage of the vulnerability in bash. http://127.0.0.1:8080/?mycmd=”() { :; }; <command here>” The PHP page calls the bash script, and passes it the “mycmd” variable as the scripts first argument. The argument is then exported in the variable named “vardec”. The variable “vardec” will now be passed to any subsequent environments (bash shells) that the script calls. At this point, the variable contains the string “() { :; }; <command here>”. The vulnerability is executed upon when the bashcgi.sh script executes the following line: bash -c "echo <br>Bash Executed</br>" The called bash shell parses the string trailing the function in the “vardec” variable and executes the code as a command.

Exploit Flow

As seen in our example, in order to achieve remote execution, specific conditions must be met. We start with a network service (HTTP) executing code (PHP), which passes un-sanitized data (PHP) to a bash environment (bash script). Inside the bash script, the un-sanitized variable must be exported, and a subsequent bash shell must be spawned in order for the arbitrary code to be parsed and executed. Graphic6

Impact

At this point you may be thinking, “What’s the big deal? It takes a lot for remote execution to occur in this case.” The impact of this vulnerability goes well beyond web servers. An exported variable containing un-sanitized user input can exploit any network service that executes a child bash shell. This is entirely dependent on the interpreting services that handle user input. In the case of Apache’s mod_cgi and mod_cgid handlers, execution can be achieved if the script is written in bash or if the script calls a bash shell. The remote vulnerability is presented in the way user input is handled. With the right conditions, a malformed cookie may even contain data used to gain remote execution on a host web server. User content stored in a database may also be used in a script variable that could possibly lead to inadvertent execution. Within 24 hours of the CVE disclosure, CrowdStrike observed multiple tactics to gain remote access to hosts, including attempted downloading and execution of Unix based RATs, netcat and /dev/tcp reverse shell commands, webshell uploads, and multiple PERL based IRC bots. It is imperative that network operators remain vigilant in the next couple of weeks while software developers provide patches to fix this far-reaching vulnerability.

Recommendations for Mitigation

Outside of telling everyone to patch, patch, and patch again; there are a few tasks that can be performed to help check for and mitigate possible attack vectors and detect successful attacks.
  • Shellshock type attacks can be avoided by not processing user data directly as variables in web/bash code. An example of this could be to base64 encode user input as it is stored in a variable.
  • By sanitizing user input and removing un-needed characters, developers can disrupt an attack before it takes place.
    • bash example:
      • x=$(echo "$1"| tr -d "\(\) { :. }")
      • export x
  • Utilize the free CrowdStrike Shellshock Scanner created by our own Robin Kier.
  • Monitor logs for evidence of attempted or successful command execution.
    • egrep "\(\) \{ :\; \}" logs.txt
    • See the apache access log/mail alert sample script below:

 

#!/bin/bash #Shellshock Log Monitor v1.0 for Apache Access Log #Copyright CrowdStrike 2014 #This script is designed to be executed every minute using your systems crontab # * * * * * /bin/bash /usr/bin/shockmon.sh 2>> ~/shockmon_err.log #configurable variables# #You can modify which log file is monitored as long as log matches the date format in the sample provided below. #Sample apache access log date format: 06/Oct/2014:17:25:22 log_to_check=/var/log/httpd-access.log tdir=/tmp/ #tdir can also use /dev/shm/ on appropriate systems email=your.email@yourdomain.com ######################## #Other Variables sleep 2 dcheck=$(date) dategrep="$(echo $dcheck|awk '{print $3}')/$(echo $dcheck | awk '{print $2}')/....:$(echo $dcheck|awk '{print $4}'|cut -d":" -f1):$(($(echo $dcheck|awk '{print $4}'|cut -d":" -f2|cut -d":" -f1)-1))" #Could likely do this all with awk or sed, but it works! messagefile=$tdir$RANDOM$RANDOM.shellshockmsg.txt IFS=$'\n' ######################## egrep $dategrep $log_to_check | egrep "\(\) \{ :\; \}" >> $messagefile checklog=$( egrep "\(\) \{ :\; \}" $messagefile | wc -l) if << "$checklog" -gt "0" >>;then messagefile2=$messagefile.2 for i in $(cat $messagefile);do echo "$i " >> $messagefile2;done #this assumes you have your *nix mail command setup properly mail -s "$(echo -e "Shellshock Alert\nFrom: ShellshockAlert <shellshock_do_not_reply@127.0.0.1>\nContent-Type: text/html\n")" $email < $messagefile2 rm $messagefile2 fi rm $messagefile
  • Monitoring network traffic for the string

     

    “() {“.
    • Many snort and suricata rules already exist for detecting this string in network traffic. CrowdStrike’s Josh Liburdi has released a Bro script to aid in detection of attempted and successful exploitation.
# Monitors traffic for Shellshock exploit attempts
# When exploits are seen containing IP addresses and domain values, monitors traffic for 60 minutes watching for endpoints to connect to IP addresses and domains seen in exploit attempts
# CrowdStrike 2014

@load base/frameworks/notice
@load base/protocols/http
@load base/protocols/dhcp

module CrowdStrike;

export {
  redef enum Notice::Type += {
    Shellshock_DHCP,
    Shellshock_HTTP,
    Shellshock_Successful_Conn
  };
}

const shellshock_pattern = /\x28\x29\x20\x7b\x20/ &redef;

const shellshock_commands = /wget/ | /curl/;

global shellshock_servers: set &create_expire=60min;
global shellshock_hosts: set &create_expire=60min;

# function to locate and extract domains seen in shellshock exploit attempts
function find_domain(ss: string)
{
local parts1 = split_all(ss,/<<:space:>>/);
for ( p in parts1 )
  if ( "http" in parts1

) { local parts2 = split_all(parts1

,/\//); local output = parts2<5>; } if ( output !in shellshock_hosts ) add shellshock_hosts; } # function to locate and extract IP addresses seen in shellshock exploit attempts function find_ip(ss: string): bool { local b = F; local remote_servers = find_ip_addresses(ss); if ( |remote_servers| > 0 ) { b = T; for ( rs in remote_servers ) { local s = to_addr(remote_servers); if ( s !in shellshock_servers ) add shellshock_servers; } } return b; } # event to identify shellshock HTTP exploit attempts event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=3 { if ( ! is_orig ) return; if ( shellshock_pattern !in value ) return; # generate a notice of the HTTP exploit attempt NOTICE(<$note=Shellshock_HTTP, $conn=c, $msg=fmt("Host %s may have attempted a shellshock HTTP exploit against %s", c$id$orig_h, c$id$resp_h), $sub=fmt("Command: %s",value), $identifier=cat(c$id$orig_h,c$id$resp_h,value)>); # check the exploit attempt for IP addresses # if an IP address is found, then do not look for domains if ( find_ip(value) == T ) return; # check the exploit attempt for domains if ( shellshock_commands in value ) find_domain(value); } # event to identify shellshock DHCP exploit attempts event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, router: dhcp_router_list, lease: interval, serv_addr: addr, host_name: string) { if ( shellshock_pattern !in host_name ) return; # generate a notice of the DHCP exploit attempt NOTICE(<$note=Shellshock_DHCP, $conn=c, $msg=fmt("Host %s may have attempted a shellshock DHCP exploit against %s", c$id$orig_h, c$id$resp_h), $sub=fmt("Command: %s",host_name), $identifier=cat(c$id$orig_h,c$id$resp_h,host_name)>); # check the exploit attempt for IP addresses # if an IP address is found, then do not look for domains if ( find_ip(host_name) == T ) return; # check the exploit attempt for domains if ( shellshock_commands in host_name ) find_domain(host_name); } # event to identify endpoints connecting to domains seen in shellshock exploit attempts event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=5 { if ( name != "HOST" ) return; if ( value in shellshock_hosts ) # generate a notice of HTTP connection to domain seen in exploit attempts NOTICE(<$note=Shellshock_Successful_Conn, $conn=c, $msg=fmt("Host %s connected to a domain seen in a shellshock exploit", c$id$orig_h), $sub=fmt("Domain: %s",value), $identifier=cat(c$id$orig_h,value)>); } # event to identify endpoints connecting to IP addresses seen in shellshock exploit attempts event connection_state_remove(c: connection) { if ( c$id$resp_h in shellshock_servers ) # generate a notice of connection to IP address seen in exploit attempts NOTICE(<$note=Shellshock_Successful_Conn, $conn=c, $msg=fmt("Host %s connected to an IP address seen in a shellshock exploit", c$id$orig_h), $sub=fmt("IP address: %s",c$id$resp_h), $identifier=cat(c$id$orig_h,c$id$resp_h)>); }

 

Breaches Stop Here