Shellshock Bug : How to Patch bash Manually

I have a number of Linux system, including several virtual machines with various distributions running everything from test web servers to a Minecraft server for my daughter, and a Raspberry Pi that gets its SD card swapped out all the time depending on what I need it to do. While most of these systems can be automatically updated via yum or apt-get to install new versions of packages, a few are running distributions for which update packages are not readily available.

This doesn’t mean that I can’t secure them against the recently discovered Shellshock vulnerability. I just needed to compile the bash software from the source code, including the Shellshock patches, and install it. That is actually not nearly as complicated as it might sound. Here’s how to do it:

Note: As with any complex issue, you may run into different configurations or conditions. Test it in a test environment before doing it in production, etc. Please understand what you are doing before you attempt to replace your shell. While this process is fairly straightforward, we here at the Twenty Sided Blog cannot be responsible for any issues arising from following the process in this post.

 

Also, please note that the test command (env ‘x=() { :;}; echo vulnerable’ ‘BASH_FUNC_x()=() { :;}; echo vulnerable’ bash -c “echo test”) is a single line, though it might appear wrapped in the text below.

 

To begin, determine which version of bash you have on your system:

$ bash --version
GNU bash, version 3.00.15(1)-release (i686-pc-linux-gnu)
Copyright (C) 2004 Free Software Foundation, Inc.

This version of bash is vulnerable to the Shellshock exploit, which we can see when we execute the test command The first two lines here are all one command line – the text is wrapping in the code format box below – everything up to “echo test” is part of the line to test the shell:

$ env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test"
vulnerable
bash: BASH_FUNC_x(): line 0: syntax error near unexpected token `)'
bash: BASH_FUNC_x(): line 0: `BASH_FUNC_x() () { :;}; echo vulnerable'
bash: error importing function definition for `BASH_FUNC_x'
test

(note the “vulnerable” printed as the first line after the command).

Since we know what version of bash we are going to update, we now need to visit the bash home page to grab the source code. The actual download area for the bash source is located here : http://ftp.gnu.org/gnu/bash/. This directory will list .tar.gz files for many versions of the bash shell, and you will want to download the version that your are going to be installing and patching. In our case, that is 3.0, so we will create a folder in our user’s home directory to download it via wget:

$ cd ~
$ mkdir bash
$ cd bash
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz

Note that I have chosen the base 3.0 version here. There is also a “3.0.16” version that we could use as our starting point to reduce the number of patches we need to apply, but we will go ahead and do them all. Also, depending on its age, your system might have a different base version of bash (2, 3, 3.2, 4, etc). I’m simply matching what I already have and applying the latest patches, and not dealing with version upgrades at this point in time. If your version is different, substitute the version numbering in the commands below – everything else should work the same way.

Once we have the source downloaded, we need to extract it:

$ tar xvfz bash-3.0.tar.gz

This will create a “bash-3.0” folder under your current location and place the source code there. Before we visit the source code, however, we need to download the patches. Create a new folder for them under the “bash” base folder and change to that folder:

$ mkdir patches
$ cd patches

Next, we need to look at the bash FTP site again and determine what patches are available. One of the sub folders of http://ftp.gnu.org/gnu/bash/ is called “bash-3.0-patches”, and inside you can see that there are 19 patches for bash 3.0, with 17, 18, and 19 being from late September 2014 to address Shellshock. We need to download ALL of the patches, since we will need to apply all of them to the source code. Again, we can use wget to do this. (Hint: after typing the first one, just arrow up and change the patch number until you have them all)

$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-001
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-002
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-003
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-004
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-005
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-006
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-007
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-008
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-009
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-010
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-011
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-012
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-013
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-014
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-015
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-016
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-017
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-018
$ wget http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-019

Note: depending on when you are reading this, there may be more than 19 patches available. Be sure to get them all!

Now that we have them all,  we need to apply them to the source code. Currently, we have a “bash” folder under the home directory, with two subfolders: “patches” and “bash-3.0”. Lets apply the first patch:

$ cd ../bash-3.0
$ patch -p0 < ../patches/bash30-001
patching file arrayfunc.c
patching file subst.c
patching file variables.c
patching file pcomplete.c
patching file array.c
patching file patchlevel.h
patching file tests/dbg-support.tests

We just have to run the “patch” command now for all of the remaining patches. Again, just arrow up and change the number. Be sure to apply them in order!

$ patch -p0 < ../patches/bash30-002
$ patch -p0 < ../patches/bash30-003
$ patch -p0 < ../patches/bash30-004
$ patch -p0 < ../patches/bash30-005
$ patch -p0 < ../patches/bash30-006
$ patch -p0 < ../patches/bash30-007
$ patch -p0 < ../patches/bash30-008
$ patch -p0 < ../patches/bash30-009
$ patch -p0 < ../patches/bash30-010
$ patch -p0 < ../patches/bash30-011
$ patch -p0 < ../patches/bash30-012
$ patch -p0 < ../patches/bash30-013
$ patch -p0 < ../patches/bash30-014
$ patch -p0 < ../patches/bash30-015
$ patch -p0 < ../patches/bash30-016
$ patch -p0 < ../patches/bash30-017
$ patch -p0 < ../patches/bash30-018
$ patch -p0 < ../patches/bash30-019

Note: I’ve left out the output of each line. Each time you run the patch command you should see a report of the files that were changed by the patch.

All that is left to do now is to compile and install the shell. Begin by running configure to set up the build options:

$ ./configure
checking for ...
{ LOTS of text omitted here }
configure: creating ./config.status
config.status: creating Makefile
config.status: creating builtins/Makefile
config.status: creating lib/readline/Makefile
config.status: creating lib/glob/Makefile
config.status: creating lib/intl/Makefile
config.status: creating lib/malloc/Makefile
config.status: creating lib/sh/Makefile
config.status: creating lib/termcap/Makefile
config.status: creating lib/tilde/Makefile
config.status: creating doc/Makefile
config.status: creating support/Makefile
config.status: creating po/Makefile.in
config.status: creating examples/loadables/Makefile
config.status: creating examples/loadables/perl/Makefile
config.status: creating pathnames.h
config.status: creating config.h
config.status: executing default-1 commands
config.status: creating po/POTFILES
config.status: creating po/Makefile
config.status: executing default commands

This will take a minute or so, and generate lots of interesting output about what your system does and doesn’t support. In the end, you want to see the config.status messages as above.

Next, we invoke the compiler to build the executable:

$ make
{Again, LOTS of stuff omitted}
ls -l bash
-rwxr-xr-x 1 webtester webtester 1890301 Sep 28 13:33 bash
size bash
text data bss dec hex filename
579406 22424 19004 620834 97922 bash

This will compile the actual C code of the bash shell, resulting (assuming you have no compilation errors) in ending output similar to the above, telling you about the executable that got produced by the compiler.

We can now test the compiled shell:

$ ./bash
bash-3.00$ bash --version
GNU bash, version 3.00.19(1)-release (i686-pc-linux-gnu)
Copyright (C) 2004 Free Software Foundation, Inc.

$ env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test"
test

We can see that we have version 3.00.19 of the bash shell, and that we no longer get the “vulnerable” message when executing the test string. However, this shell only exists under our compilation directory at this point. If we want to make the shell active for the rest of the system, we need to install it, which we need to do as the superuser:

$ sudo make install

This command will copy the new executable (along with the documentation) to their permanent homes on the system, and your shell is updated!

If and when future patches are released, you can simply download them, patch the source, and start the make process over again (you shouldn’t need to rerun ./configure, though it won’t hurt anything if you do).

Bookmark the permalink.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • Advertisement