How to patch your vulnerable OS X to latest bash issue

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] = ‘’;
+
+   string_length = strlen (string);
+   temp_string = (char *)xmalloc (name_length + 1 + string_length + 1);
+   memcpy (temp_string, temp_name, name_length);
+   temp_string[name_length] = ‘ ‘;
+   memcpy (temp_string + name_length + 1, string, string_length + 1);

parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);

–      /* Ancient backwards compatibility.  Old versions of bash exported
–         functions like name()=() {…} */
–      if (name[char_index – 1] == ‘)’ && name[char_index – 2] == ‘(‘)
–        name[char_index – 2] = ‘’;
+    /* Don’t import function names that are invalid identifiers from the
+       environment, though we still allow them to be defined as shell
+       variables. */
+   if (legal_identifier (temp_name))
+     parse_and_execute (temp_string, temp_name,
+            SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);

–      if (temp_var = find_function (name))
+      if (temp_var = find_function (temp_name))
{
VSETATTR (temp_var, (att_exported|att_imported));
array_needs_making = 1;
@@ -333,9 +350,8 @@
else
report_error (_(“error importing function definition for `%s'”), name);

–      /* ( */
–      if (name[char_index – 1] == ‘)’ && name[char_index – 2] == ‘’)
–        name[char_index – 2] = ‘(‘;        /* ) */
+   /* Restore the original suffix. */
+   temp_name[name_length] = FUNCDEF_SUFFIX[0];
}
#if defined (ARRAY_VARS)
#  if 0
@@ -2213,7 +2229,7 @@
var->context = variable_context;    /* XXX */

INVALIDATE_EXPORTSTR (var);
–  var->exportstr = mk_env_string (name, value);
+  var->exportstr = mk_env_string (name, value, 0);

array_needs_making = 1;

@@ -3003,22 +3019,44 @@
/*                                    */
/* **************************************************************** */

+/* Returns the string NAME=VALUE if !FUNCTIONP or if VALUE == NULL (in
+   which case it is treated as empty).  Otherwise, decorate NAME with
+   FUNCDEF_PREFIX and FUNCDEF_SUFFIX, and return a string of the form
+   FUNCDEF_PREFIX NAME FUNCDEF_SUFFIX = VALUE (without spaces).  */
static inline char *
-mk_env_string (name, value)
+mk_env_string (name, value, functionp)
const char *name, *value;
+     int functionp;
{
–  int name_len, value_len;
–  char    *p;
+  size_t name_len, value_len;
+  char *p, *q;

name_len = strlen (name);
value_len = STRLEN (value);
–  p = (char *)xmalloc (2 + name_len + value_len);
–  strcpy (p, name);
–  p[name_len] = ‘=’;
+  if (functionp && value != NULL)
+    {
+      p = (char *)xmalloc (FUNCDEF_PREFIX_LEN + name_len + FUNCDEF_SUFFIX_LEN
+        + 1 + value_len + 1);
+      q = p;
+      memcpy (q, FUNCDEF_PREFIX, FUNCDEF_PREFIX_LEN);
+      q += FUNCDEF_PREFIX_LEN;
+      memcpy (q, name, name_len);
+      q += name_len;
+      memcpy (q, FUNCDEF_SUFFIX, FUNCDEF_SUFFIX_LEN);
+      q += FUNCDEF_SUFFIX_LEN;
+    }
+  else
+    {
+      p = (char *)xmalloc (name_len + 1 + value_len + 1);
+      memcpy (p, name, name_len);
+      q = p + name_len;
+    }
+  q[0] = ‘=’;
+
if (value && *value)
–    strcpy (p + name_len + 1, value);
+    memcpy (q + 1, value, value_len + 1);
else
–    p[name_len + 1] = ‘’;
+    q[1] = ‘’;
return (p);
}

@@ -3093,7 +3131,7 @@
/* Gee, I’d like to get away with not using savestring() if we’re
using the cached exportstr… */
list[list_index] = USE_EXPORTSTR ? savestring (value)
–                       : mk_env_string (var->name, value);
+                       : mk_env_string (var->name, value, function_p (var));

if (USE_EXPORTSTR == 0)
SAVE_EXPORTSTR (var, list[list_index]);
—– CUT HERE ——

Leave a Reply

Your email address will not be published. Required fields are marked *

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