How to patch your vulnerable OS X to latest bash issue:
Untested!
Download source package from:
https://opensource.apple.com/tarballs/bash/bash-92.tar.gz
Apply the patch below.
Open the bash.xcodeproj in Xcode.
Compile (you probably want to modify the Xcode project to codesign if you have a certificate).
Copy the binaries (bash and sh) to /bin/bash and /bin/sh.
Enjoy being an advanced Mac OS X user!
Patches from latest RedHat packages.
fg!
$ env x='() { :;}; echo vulnerable’ bash -c “echo this is a test”
this is a test
$ env X='() { (a)=>’ bash -c “echo date”; cat echo
date
cat: echo: No such file or directory
—– CUT HERE ——
diff -ur bash-92/bash-3.2/builtins/common.h bash-92-patched/bash-3.2/builtins/common.h
— bash-92/bash-3.2/builtins/common.h 2009-06-12 00:29:43.000000000 +0100
+++ bash-92-patched/bash-3.2/builtins/common.h 2014-09-26 10:46:58.000000000 +0100
@@ -31,6 +31,8 @@
#define SEVAL_NOHIST 0x004
#define SEVAL_NOFREE 0x008
#define SEVAL_RESETLINE 0x010
+#define SEVAL_FUNCDEF 0x080 /* only allow function definitions */
+#define SEVAL_ONECMD 0x100 /* only allow a single command */
/* Flags for describe_command, shared between type.def and command.def */
#define CDESC_ALL 0x001 /* type -a */
diff -ur bash-92/bash-3.2/builtins/evalstring.c bash-92-patched/bash-3.2/builtins/evalstring.c
— bash-92/bash-3.2/builtins/evalstring.c 2009-06-12 00:29:43.000000000 +0100
+++ bash-92-patched/bash-3.2/builtins/evalstring.c 2014-09-26 10:46:22.000000000 +0100
@@ -234,6 +234,14 @@
{
struct fd_bitmap *bitmap;
+ if ((flags & SEVAL_FUNCDEF) && command->type != cm_function_def)
+ {
+ internal_warning (“%s: ignoring function definition attempt”, from_file);
+ should_jump_to_top_level = 0;
+ last_result = last_command_exit_value = EX_BADUSAGE;
+ break;
+ }
+
bitmap = new_fd_bitmap (FD_BITMAP_SIZE);
begin_unwind_frame (“pe_dispose”);
add_unwind_protect (dispose_fd_bitmap, bitmap);
@@ -291,6 +299,9 @@
dispose_command (command);
dispose_fd_bitmap (bitmap);
discard_unwind_frame (“pe_dispose”);
+ if (flags & SEVAL_ONECMD)
+ break;
+
}
}
else
diff -ur bash-92/bash-3.2/parse.y bash-92-patched/bash-3.2/parse.y
— bash-92/bash-3.2/parse.y 2013-01-22 01:37:34.000000000 +0000
+++ bash-92-patched/bash-3.2/parse.y 2014-09-26 10:48:52.000000000 +0100
@@ -253,9 +253,21 @@
/* Variables to manage the task of reading here documents, because we need to
defer the reading until after a complete command has been collected. */
-static REDIRECT *redir_stack[10];
+static REDIRECT **redir_stack;
int need_here_doc;
+/* Pushes REDIR onto redir_stack, resizing it as needed. */
+static void
+push_redir_stack (REDIRECT *redir)
+{
+ /* Guard against oveflow. */
+ if (need_here_doc + 1 > INT_MAX / sizeof (*redir_stack))
+ abort ();
+ redir_stack = xrealloc (redir_stack,
+ (need_here_doc + 1) * sizeof (*redir_stack));
+ redir_stack[need_here_doc++] = redir;
+}
+
/* Where shell input comes from. History expansion is performed on each
line when the shell is interactive. */
static char *shell_input_line = (char *)NULL;
@@ -424,13 +436,13 @@
{
redir.filename = $2;
$$ = make_redirection (0, r_reading_until, redir);
– redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| NUMBER LESS_LESS WORD
{
redir.filename = $3;
$$ = make_redirection ($1, r_reading_until, redir);
– redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| LESS_LESS_LESS WORD
{
@@ -487,14 +499,14 @@
redir.filename = $2;
$$ = make_redirection
(0, r_deblank_reading_until, redir);
– redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| NUMBER LESS_LESS_MINUS WORD
{
redir.filename = $3;
$$ = make_redirection
($1, r_deblank_reading_until, redir);
– redir_stack[need_here_doc++] = $$;
+ push_redir_stack ($$);
}
| GREATER_AND ‘-‘
{
@@ -2503,6 +2515,8 @@
FREE (word_desc_to_read);
word_desc_to_read = (WORD_DESC *)NULL;
+ eol_ungetc_lookahead = 0;
+
last_read_token = ‘n’;
token_to_read = ‘n’;
}
@@ -3767,7 +3781,7 @@
case CASE:
case SELECT:
case FOR:
– if (word_top < MAX_CASE_NEST)
+ if (word_top + 1 < MAX_CASE_NEST)
word_top++;
word_lineno[word_top] = line_number;
break;
diff -ur bash-92/bash-3.2/patchlevel.h bash-92-patched/bash-3.2/patchlevel.h
— bash-92/bash-3.2/patchlevel.h 2013-01-22 01:37:34.000000000 +0000
+++ bash-92-patched/bash-3.2/patchlevel.h 2014-09-26 10:44:46.000000000 +0100
@@ -25,6 +25,6 @@
regexp `^#define[ ]*PATCHLEVEL’, since that’s what support/mkversion.sh
looks for to find the patch level (for the sccs version string). */
-#define PATCHLEVEL 51
+#define PATCHLEVEL 52
#endif /* _PATCHLEVEL_H_ */
diff -ur bash-92/bash-3.2/variables.c bash-92-patched/bash-3.2/variables.c
— bash-92/bash-3.2/variables.c 2009-06-12 00:29:43.000000000 +0100
+++ bash-92-patched/bash-3.2/variables.c 2014-09-26 10:42:22.000000000 +0100
@@ -241,7 +241,7 @@
static void propagate_temp_var __P((PTR_T));
static void dispose_temporary_env __P((sh_free_func_t *));
-static inline char *mk_env_string __P((const char *, const char *));
+static inline char *mk_env_string __P((const char *, const char *, int));
static char **make_env_array_from_var_list __P((SHELL_VAR **));
static char **make_var_export_array __P((VAR_CONTEXT *));
static char **make_func_export_array __P((void));
@@ -274,6 +274,13 @@
#endif
}
+/* Prefix and suffix for environment variable names which contain
+ shell functions. */
+#define FUNCDEF_PREFIX “BASH_FUNC_”
+#define FUNCDEF_PREFIX_LEN (strlen (FUNCDEF_PREFIX))
+#define FUNCDEF_SUFFIX “()”
+#define FUNCDEF_SUFFIX_LEN (strlen (FUNCDEF_SUFFIX))
+
/* Initialize the shell variables from the current environment.
If PRIVMODE is nonzero, don’t import functions from ENV or
parse $SHELLOPTS. */
@@ -309,23 +316,33 @@
/* If exported function, define it now. Don’t import functions from
the environment in privileged mode. */
– if (privmode == 0 && read_but_dont_execute == 0 && STREQN (“() {“, string, 4))
– {
– string_length = strlen (string);
– temp_string = (char *)xmalloc (3 + string_length + char_index);
–
– strcpy (temp_string, name);
– temp_string[char_index] = ‘ ‘;
– strcpy (temp_string + char_index + 1, string);
+ if (privmode == 0 && read_but_dont_execute == 0
+ && STREQN (FUNCDEF_PREFIX, name, FUNCDEF_PREFIX_LEN)
+ && STREQ (name + char_index – FUNCDEF_SUFFIX_LEN, FUNCDEF_SUFFIX)
+ && STREQN (“() {“, string, 4))
+ {
+ size_t name_length
+ = char_index – (FUNCDEF_PREFIX_LEN + FUNCDEF_SUFFIX_LEN);
+ char *temp_name = name + FUNCDEF_PREFIX_LEN;
+ /* Temporarily remove the suffix. */
+ temp_name[name_length] = ‘