Instantly share code, notes, and snippets.

Embed
What would you like to do?
Compile Bash 3.2 from source for Debian Lenny to patch the shellshock vulnerabilities (CVE-2014-6271, CVE-2014-7169, CVE-2014-6277, CVE-2014-6278, CVE-2014-7186, CVE-2014-7187)
# inspired by http://askubuntu.com/a/528171 and the comments below
# build bash 3.2, though this should work for other versions as well
BASH_MAJOR=3
BASH_MINOR=2
# prerequisites
sudo apt-get install build-essential gettext bison
# get bash source
mkdir src && cd src
wget https://ftp.gnu.org/gnu/bash/bash-$BASH_MAJOR.$BASH_MINOR.tar.gz
tar zxvf bash-$BASH_MAJOR.$BASH_MINOR.tar.gz
cd bash-$BASH_MAJOR.$BASH_MINOR
# download, verify, and apply all available patches, which as of 2014-10-02
# include patches for CVE-2014-6271, CVE-2014-7169, CVE-2014-6277, CVE-2014-6278
# CVE-2014-7186, and CVE-2014-7187.
wget -nv -r 1 -nH -nd -np https://ftp.gnu.org/gnu/bash/bash-$BASH_MAJOR.$BASH_MINOR-patches/
wget -nv https://ftp.gnu.org/gnu/gnu-keyring.gpg
for i in bash$BASH_MAJOR$BASH_MINOR-???; do
if gpg --verify --keyring ./gnu-keyring.gpg $i.sig; then
if ! patch -p0 < $i; then
echo "patch $i failed"
exit 1
fi
else
echo "patch $i has a bad signature"
exit 2
fi
done
# compile and install to /usr/local/bin/bash
./configure && make
sudo make install
# point /bin/bash to the new binary
if /usr/local/bin/bash -c 'true'; then
if [ ! -f /bin/bash.old ]; then
sudo mv /bin/bash /bin/bash.old
sudo ln -s /usr/local/bin/bash /bin/bash
fi
else
echo "bash not installed correctly!"
exit 3
fi
# test each of the exploits on the old version of bash
echo "OLD BASH:"
env x='() { :;}; echo VULNERABLE to CVE-2014-6271' /bin/bash.old -c echo
env x='() { (a)=>\' /bin/bash.old -c "echo echo TEST" 2>/dev/null; cat echo 2>/dev/null; rm -f ./echo; echo "If you see 'echo TEST' above you are ok, if you just see 'TEST' you are VULNERABLE to CVE-2014-7169"
/bin/bash.old -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' || echo "VULNERABLE to CVE-2014-7186"
(for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do echo done ; done) | /bin/bash.old || echo "VULNERABLE to CVE-2014-7187"
# test each of the exploits on the new version of bash
echo "NEW BASH:"
env x='() { :;}; echo Vulnerable to CVE-2014-6271' bash -c echo
env x='() { (a)=>\' bash -c "echo echo TEST" 2>/dev/null; cat echo 2>/dev/null; rm -f ./echo; echo "If you see 'echo TEST' above you are ok, if you just see 'TEST' you are VULNERABLE to CVE-2014-7169"
bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' || echo "VULNERABLE to CVE-2014-7186"
(for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do echo done ; done) | bash || echo "VULNERABLE to CVE-2014-7187"
echo "NOTE: CVE-2014-6277 and CVE-2014-6278 should be mitigated by these patches as well, but there is not yet a test for them."
@href

This comment has been minimized.

href commented Sep 25, 2014

I added signature verification for the patches:
https://gist.github.com/href/54859127c183f67f947f

@stephankoelle

This comment has been minimized.

stephankoelle commented Sep 25, 2014

Thanks a log.
I had to install make, gcc, patch

@Wol

This comment has been minimized.

Wol commented Sep 25, 2014

May want to check that it's built correctly before doing the mv line. I didn't have make installed, so the compile step failed, but then it moved my existing bash to bash.old and didn't replace it with a newer one!

@BKnights

This comment has been minimized.

BKnights commented Sep 25, 2014

Thank you!

@powermate

This comment has been minimized.

powermate commented Sep 25, 2014

I patched it, and when I tested the actual bash the response was:

env: bash: No such file or directory

Is it normal?

Thank you.

@tuwxyz

This comment has been minimized.

tuwxyz commented Sep 25, 2014

@panayotb

This comment has been minimized.

panayotb commented Sep 26, 2014

To apply the final patch:

  1. After it gets uploaded to http://ftp.gnu.org/gnu/bash/bash-3.2-patches/ (look for bash32-053):

Change line

for i in $(seq -f "%03g" 1 52); do

to

for i in $(seq -f "%03g" 1 53); do

  1. If you do not want to wait, you can download the patch from http://seclists.org/oss-sec/2014/q3/734 (Attachment: bash32-053)

Manually execute the commands from the original script until you get to

compile and install to /usr/local/bin/bash

Then do:

wget http://seclists.org/oss-sec/2014/q3/att-734/bash32-053.bin
mv bash32-053.bin bash32-053
patch -p0 < bash32-053

Then continue with the next commands in the original script

@ChrisRuss

This comment has been minimized.

ChrisRuss commented Sep 26, 2014

I made a temporary solution, feel free to use it while waiting:
https://gist.github.com/ChrisRuss/f2eb63686540ed9b00f6

@mattwhite

This comment has been minimized.

Owner

mattwhite commented Sep 26, 2014

Ok, I have updated the gist with the improvements from @href, @stephankoelle, and @Wol. The patch for CVE-2014-7169 is available now, so I've included that as well.

@tuwxyz

This comment has been minimized.

tuwxyz commented Sep 26, 2014

Thanks for updates. I really appreciate it.

@sodabrew

This comment has been minimized.

sodabrew commented Sep 28, 2014

This is awesome. Could you update line 15 once again?
There's now bash32-054 27-Sep-2014 22:37 6.5K on ftp.gnu.org.

@methodvon

This comment has been minimized.

methodvon commented Sep 29, 2014

Thank you for this.

@mattwhite

This comment has been minimized.

Owner

mattwhite commented Sep 29, 2014

Thanks @sodabrew, I updated it.

@Archetrix

This comment has been minimized.

Archetrix commented Sep 29, 2014

Why not going for a full download of all patches? Current solution needs fixing for every new patch that appears. My idea is to download the full patches folder and someone at ycombinator (https://news.ycombinator.com/item?id=8374948) had the same idea:

  wget -r -l1 -nH -nd -np http://ftp.gnu.org/gnu/bash/bash-4.3-patches/ && rm *.sig index.html
  cd bash-4.3
  for i in ../bash43-*; do patch -p0 < $i; done

Maybe get rid of the static bash-version, too so one can apply the same script to some newer (or older) bash later on.
Above code is just deleting the *.sig, but one can easily extend the for-loop like this (untested):

  for i in  ../bash43-???; do
    if gpg --verify --keyring ./gnu-keyring.gpg $i.sig; then 
      if ! patch -p0 < $i; then
        echo "patch $i failed"
        exit 1 
      fi 
    else 
      echo "patch $i has a bad signature!"
      exit 2
    fi
  done
@tonyflint

This comment has been minimized.

tonyflint commented Sep 29, 2014

This is awesome, thanks.

Added auto-finding of latest patch level: https://gist.github.com/tonyflint/fddb5a3c669b8ae7d897

@mattwhite

This comment has been minimized.

Owner

mattwhite commented Sep 30, 2014

Thanks for the suggestions @Archetrix amd @tonyflint. I have added downloading all available patches and put the bash version into env variables. I have also added tests for some of the subsequent bash vulnerabilities.

@Archetrix

This comment has been minimized.

Archetrix commented Sep 30, 2014

I'm still full of ideas :)

Is it intentionally to compile with default settings?

./configure --prefix=/usr --bindir=/bin --sbindir=/sbin --sysconfdir=/etc && make

@Archetrix

This comment has been minimized.

Archetrix commented Sep 30, 2014

Oh i just saw your tests at the end. I've got a test for the latest patch:

env ls="() { echo 'Game over'; }" bash -c ls

Basically if you get "ls" of your current folder it's OK if you get "Game over" you're screwed 😉
Or do this:

env date="() { echo 'doomsday'; }" bash -c date

If it returns a date it's ok if not .... 😄

@Archetrix

This comment has been minimized.

Archetrix commented Sep 30, 2014

I fun-tested bash 2.05b (has recent patches)
There are really old patches which don't have a .sig file.

@piccaso

This comment has been minimized.

piccaso commented Sep 30, 2014

thanks to all who helped putting this together, saves a lot of time!
btw.: Consider removing/archiving bash.old

@section1

This comment has been minimized.

section1 commented Oct 1, 2014

Hi thanks for the script but im having problem with the last two tests..

dtest:/bash# bash --version
GNU bash, version 3.2.54(1)-release (i686-pc-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
dtest:
/bash# echo $BASH_VERSION
3.2.54(1)-release
dtest:/bash#
dtest:
/bash# bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' || echo "VULNERABLE to CVE-2014-7186"
Segmentation fault
VULNERABLE to CVE-2014-7186
dtest:/bash# (for x in {1..200} ; do echo "for x$x in ; do :"; done; for x in {1..200} ; do echo done ; done) | bash || echo "VULNERABLE to CVE-2014-7187"
bash: line 129: syntax error near x129' bash: line 129:for x129 in ; do :'
VULNERABLE to CVE-2014-7187
dtest:
/bash#

@mattwhite

This comment has been minimized.

Owner

mattwhite commented Oct 2, 2014

@Archetrix, yes, I wanted to build in /usr/local/bin since this was a locally-compiled version of bash and not the one from the package.

@piccaso, no problem. i like keeping bash.old around in order to compare results of any new exploits found between the original and patched version.

@section1, previously the last two tests failed because bash 3.2 was not yet patched against them. however, a new patch was released on Oct 1, so you'll need to recompile bash. I can verify that the latest patch fixes the last two vulnerabilities.

@Archetrix

This comment has been minimized.

Archetrix commented Oct 2, 2014

Another …. tweak:

build bash 3.2, though this should work for other versions as well

BASH_VER=bash --version | grep 'version' | awk '{print $4};'
BASH_MAJOR=$(echo $BASH_VER | cut -d '.' -f1)
BASH_MINOR=$(echo $BASH_VER | cut -d '.' -f2)

@J-Damian

This comment has been minimized.

J-Damian commented Oct 14, 2014

Hi

I signed in here to comment THIS IMPORTANT ISSUE in your script (and forks as TonyFlint's).

The problem is the method to replace the vulnerable bash: a symbolic link to the patched bash. In my Debian 5 Lenny nodes, the /usr/local resides on A SEPARATE file system from the ROOT FILE SYSTEM, therefore /bin and /usr/local/bin reside on differente devices.

This causes an unbootable system because, in that case, the /bin/bash is not available to execute the scripts /etc/init.d/rcS, /etc/init.d/rc.

Therefore, instead of create a symbolic link, copy the patched binary bash into /bin.

Best regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment