On 28/09/12 22:30, Geoffrey Thomas wrote:
> CVE-2012-3524 is about setuid binaries linking libdbus being easily
> trickable to do bad things via a malicious PATH (for finding
> dbus-launch), or through a DBUS_* address variable using the unixexec
> address type.

Potentially-vulnerable binaries are anything that is setuid and links
either libdbus-1.so.3 (CVE-2012-3524), directly or via e.g.
libpam-systemd or libhal, or libgio-2.0.so.0 >= 2.26 (CVE-2012-4425).
squeeze's libgio-2.0 is too old to be vulnerable to this anyway (it
doesn't have a D-Bus implementation).

I consider patching the libraries to be defence-in-depth, rather than a
real solution: the real solution is for setuid binaries to clear their
caller-supplied environments before they call into non-trivial
libraries. Nevertheless, patching libdbus is the most expedient way to
become less exploitable.

Security team: do you want to handle this for squeeze as a security
update, or a normal stable update? I attach a proposed debdiff;
s/stable/stable-security/ if desired.

The "unixexec" attack vector for arbitrary code execution doesn't work
for squeeze, because that feature is too new. I believe the dbus-launch
attack vector for arbitrary code execution only works if you have
libdbus-1-3 and a vulnerable setuid binary that links it, but not
dbus-x11. There are also some less severe attack vectors involving
revealing part of a normally-unreadable file via the nonce-tcp
transport, or sending the beginning of a D-Bus handshake to a
normally-unavailable Unix socket; these will work in squeeze too.

The specific binaries I'm aware of that are likely to be vulnerable in
squeeze are:

* Xorg when linked to libhal and run via /usr/bin/X (only on non-Linux,
  because it isn't linked to libdbus any more on Linux; unconfirmed)

and in wheezy:

* Xorg on non-Linux, as in squeeze
* su with libpam-systemd (unconfirmed but likely)
* sudo with libpam-systemd (unconfirmed; might be unaffected,
  since it's pretty careful with its environment)
* spice-gtk (confirmed to be vulnerable to CVE-2012-4425,
  I opened #689155)

I haven't done a whole-archive scan or anything, though.

For sid, CVE-2012-3524 is fixed by dbus/1.6.8-1.

For wheezy, it will be fixed in 1.6.8-1 if the release team let it
migrate, and/or 1.6.0-2 (not yet uploaded) if they want to go via t-p-u.
See #689148 for the release-team interaction.

To help with testing, I attach a relatively harmless version of an
exploit for this vulnerability: it creates a vulnerable setuid-nobody
binary, and tries to use it to "escalate" privileges from the real user
to nobody. It requires sudo privileges to chown/chmod the vulnerable
binary, but does not use them for the actual exploit.

The good result is if you get syslog messages like this:

Sep 29 16:27:24 archetype cve-2012-3524: begin
Sep 29 16:27:24 archetype cve-2012-3524: end

A bad result looks more like this:

cve-2012-3524: begin
evil-dbus-launch-substitute: uid=1000(smcv) gid=1000(smcv)
euid=65534(nobody) ...
cve-2012-3524: end

(you'll get up to two evil-dbus-launch-substitute lines, depending which
version(s) of the attack worked).

Regards,
    S
diffstat for dbus-1.2.24 dbus-1.2.24

 changelog                                                               |   12 
 patches/0001-CVE-2012-3524-Don-t-access-environment-variables-or-.patch |  215 ++++++++++
 patches/0002-hardening-Ensure-_dbus_check_setuid-is-initialized-t.patch |   35 +
 patches/0003-hardening-Remove-activation-helper-handling-for-DBUS.patch |   56 ++
 patches/0004-activation-helper-Ensure-DBUS_STARTER_ADDRESS-is-set.patch |   63 ++
 patches/series                                                          |    4 
 6 files changed, 385 insertions(+)

diff -Nru dbus-1.2.24/debian/changelog dbus-1.2.24/debian/changelog
--- dbus-1.2.24/debian/changelog	2011-06-14 20:09:38.000000000 +0100
+++ dbus-1.2.24/debian/changelog	2012-09-29 15:52:32.000000000 +0100
@@ -1,3 +1,15 @@
+dbus (1.2.24-4+squeeze2) stable; urgency=low
+
+  * CVE-2012-3524: apply patches from upstream 1.6.6 to avoid arbitrary
+    code execution in setuid/setgid binaries that incorrectly use libdbus
+    without first sanitizing the environment variables inherited from
+    their less-privileged caller (Closes: #689070).
+    - As per upstream 1.6.8, do not check filesystem capabilities for now,
+      only setuid/setgid, fixing regressions in certain configurations of
+      gnome-keyring
+
+ -- Simon McVittie <s...@debian.org>  Sat, 29 Sep 2012 13:33:07 +0100
+
 dbus (1.2.24-4+squeeze1) stable; urgency=low
 
   * Update Vcs-* control fields to reflect the move to git
diff -Nru dbus-1.2.24/debian/patches/0001-CVE-2012-3524-Don-t-access-environment-variables-or-.patch dbus-1.2.24/debian/patches/0001-CVE-2012-3524-Don-t-access-environment-variables-or-.patch
--- dbus-1.2.24/debian/patches/0001-CVE-2012-3524-Don-t-access-environment-variables-or-.patch	1970-01-01 01:00:00.000000000 +0100
+++ dbus-1.2.24/debian/patches/0001-CVE-2012-3524-Don-t-access-environment-variables-or-.patch	2012-09-29 15:52:32.000000000 +0100
@@ -0,0 +1,215 @@
+From 50247498f01cc88be7b2a6aef9dcefb0fe3f10f1 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <simon.mcvit...@collabora.co.uk>
+Date: Sat, 29 Sep 2012 14:09:12 +0100
+Subject: [PATCH 1/4] CVE-2012-3524: Don't access environment variables or run
+ dbus-launch when setuid
+
+This matches a corresponding change in GLib.  See
+glib/gutils.c:g_check_setuid().
+
+Some programs attempt to use libdbus when setuid; notably the X.org
+server is shipped in such a configuration. libdbus never had an
+explicit policy about its use in setuid programs.
+
+I'm not sure whether we should advertise such support.  However, given
+that there are real-world programs that do this currently, we can make
+them safer with not too much effort.
+
+Better to fix a problem caused by an interaction between two
+components in *both* places if possible.
+
+How to determine whether or not we're running in a privilege-escalated
+path is operating system specific.  Note that GTK+'s code to check
+euid versus uid worked historically on Unix, more modern systems have
+filesystem capabilities and SELinux domain transitions, neither of
+which are captured by the uid comparison.
+
+On Linux/glibc, the way this works is that the kernel sets an
+AT_SECURE flag in the ELF auxiliary vector, and glibc looks for it on
+startup.  If found, then glibc sets a public-but-undocumented
+__libc_enable_secure variable which we can use.  Unfortunately, while
+it *previously* worked to check this variable, a combination of newer
+binutils and RPM break it:
+http://www.openwall.com/lists/owl-dev/2012/08/14/1
+
+So for now on Linux/glibc, we fall back to the historical Unix version
+until we get glibc fixed.
+
+On some BSD variants, there is a issetugid() function.  On other Unix
+variants, we fall back to what GTK+ has been doing.
+
+Reported-by: Sebastian Krahmer <krah...@suse.de>
+Signed-off-by: Colin Walters <walt...@verbum.org>
+Conflicts:
+	dbus/dbus-sysdeps-unix.c
+Cherry-picked: from 9cdf87a998329e06a1ffc401829b4f01b10d2861
+Conflicts:
+	configure.in
+	dbus/dbus-sysdeps-unix.c
+	dbus/dbus-sysdeps-win.c
+---
+ configure.in             |    2 +-
+ dbus/dbus-keyring.c      |    7 ++++++
+ dbus/dbus-sysdeps-unix.c |   62 +++++++++++++++++++++++++++++++++++++++++++++-
+ dbus/dbus-sysdeps-win.c  |    6 +++++
+ dbus/dbus-sysdeps.c      |    5 ++++
+ dbus/dbus-sysdeps.h      |    1 +
+ 6 files changed, 81 insertions(+), 2 deletions(-)
+
+diff --git a/configure.in b/configure.in
+index b027f86..28c11de 100644
+--- a/configure.in
++++ b/configure.in
+@@ -430,7 +430,7 @@ AC_DEFINE_UNQUOTED(DBUS_HAVE_ATOMIC_INT_COND, [$have_atomic_inc_cond],
+ AC_SEARCH_LIBS(socket,[socket network])
+ AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)])
+ 
+-AC_CHECK_FUNCS(vsnprintf vasprintf nanosleep usleep setenv clearenv unsetenv socketpair getgrouplist fpathconf setrlimit poll)
++AC_CHECK_FUNCS(vsnprintf vasprintf nanosleep usleep setenv clearenv unsetenv socketpair getgrouplist fpathconf setrlimit poll issetugid getresuid)
+ 
+ #### Check for broken poll; taken from Glib's configure
+ 
+diff --git a/dbus/dbus-keyring.c b/dbus/dbus-keyring.c
+index 6dc1e12..0c32a4f 100644
+--- a/dbus/dbus-keyring.c
++++ b/dbus/dbus-keyring.c
+@@ -718,6 +718,13 @@ _dbus_keyring_new_for_credentials (DBusCredentials  *credentials,
+   DBusCredentials *our_credentials;
+   
+   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
++
++  if (_dbus_check_setuid ())
++    {
++      dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
++                            "Unable to create DBus keyring when setuid");
++      return NULL;
++    }
+   
+   keyring = NULL;
+   error_set = FALSE;
+diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c
+index b58d09a..d4777c5 100644
+--- a/dbus/dbus-sysdeps-unix.c
++++ b/dbus/dbus-sysdeps-unix.c
+@@ -3124,7 +3124,14 @@ _dbus_get_autolaunch_address (DBusString *address,
+   int i;
+   DBusString uuid;
+   dbus_bool_t retval;
+-  
++
++  if (_dbus_check_setuid ())
++    {
++      dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
++                            "Unable to autolaunch when setuid");
++      return FALSE;
++    }
++
+   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+   retval = FALSE;
+ 
+@@ -3510,4 +3517,57 @@ _dbus_get_is_errno_eagain_or_ewouldblock (void)
+   return errno == EAGAIN || errno == EWOULDBLOCK;
+ }
+ 
++/**
++ * **NOTE**: If you modify this function, please also consider making
++ * the corresponding change in GLib.  See
++ * glib/gutils.c:g_check_setuid().
++ *
++ * Returns TRUE if the current process was executed as setuid (or an
++ * equivalent __libc_enable_secure is available).  See:
++ * http://osdir.com/ml/linux.lfs.hardened/2007-04/msg00032.html
++ */
++dbus_bool_t
++_dbus_check_setuid (void)
++{
++  /* TODO: get __libc_enable_secure exported from glibc.
++   * See http://www.openwall.com/lists/owl-dev/2012/08/14/1
++   */
++#if 0 && defined(HAVE_LIBC_ENABLE_SECURE)
++  {
++    /* See glibc/include/unistd.h */
++    extern int __libc_enable_secure;
++    return __libc_enable_secure;
++  }
++#elif defined(HAVE_ISSETUGID)
++  /* BSD: http://www.freebsd.org/cgi/man.cgi?query=issetugid&sektion=2 */
++  return issetugid ();
++#else
++  uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
++  gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
++
++  static dbus_bool_t check_setuid_initialised;
++  static dbus_bool_t is_setuid;
++
++  if (_DBUS_UNLIKELY (!check_setuid_initialised))
++    {
++#ifdef HAVE_GETRESUID
++      if (getresuid (&ruid, &euid, &suid) != 0 ||
++          getresgid (&rgid, &egid, &sgid) != 0)
++#endif /* HAVE_GETRESUID */
++        {
++          suid = ruid = getuid ();
++          sgid = rgid = getgid ();
++          euid = geteuid ();
++          egid = getegid ();
++        }
++
++      check_setuid_initialised = TRUE;
++      is_setuid = (ruid != euid || ruid != suid ||
++                   rgid != egid || rgid != sgid);
++
++    }
++  return is_setuid;
++#endif
++}
++
+ /* tests in dbus-sysdeps-util.c */
+#diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c
+#index 02f3123..15fb5cb 100644
+#--- a/dbus/dbus-sysdeps-win.c
+#+++ b/dbus/dbus-sysdeps-win.c
+#@@ -3370,6 +3370,12 @@ _dbus_append_keyring_directory_for_credentials (DBusString      *directory,
+#   return FALSE;
+# }
+# 
+#+dbus_bool_t
+#+_dbus_check_setuid (void)
+#+{
+#+  return FALSE;
+#+}
+#+
+# /** @} end of sysdeps-win */
+# /* tests in dbus-sysdeps-util.c */
+# 
+diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c
+index ccd80cc..c3acc17 100644
+--- a/dbus/dbus-sysdeps.c
++++ b/dbus/dbus-sysdeps.c
+@@ -176,6 +176,11 @@ _dbus_setenv (const char *varname,
+ const char*
+ _dbus_getenv (const char *varname)
+ {  
++  /* Don't respect any environment variables if the current process is
++   * setuid.  This is the equivalent of glibc's __secure_getenv().
++   */
++  if (_dbus_check_setuid ())
++    return NULL;
+   return getenv (varname);
+ }
+ 
+diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h
+index 7817e04..3c374d2 100644
+--- a/dbus/dbus-sysdeps.h
++++ b/dbus/dbus-sysdeps.h
+@@ -97,6 +97,7 @@ typedef struct DBusCredentials DBusCredentials;
+ 
+ void _dbus_abort (void) _DBUS_GNUC_NORETURN;
+ 
++dbus_bool_t _dbus_check_setuid (void);
+ const char* _dbus_getenv (const char *varname);
+ dbus_bool_t _dbus_setenv (const char *varname,
+ 			  const char *value);
+-- 
+1.7.10.4
+
diff -Nru dbus-1.2.24/debian/patches/0002-hardening-Ensure-_dbus_check_setuid-is-initialized-t.patch dbus-1.2.24/debian/patches/0002-hardening-Ensure-_dbus_check_setuid-is-initialized-t.patch
--- dbus-1.2.24/debian/patches/0002-hardening-Ensure-_dbus_check_setuid-is-initialized-t.patch	1970-01-01 01:00:00.000000000 +0100
+++ dbus-1.2.24/debian/patches/0002-hardening-Ensure-_dbus_check_setuid-is-initialized-t.patch	2012-09-29 15:52:32.000000000 +0100
@@ -0,0 +1,35 @@
+From 47ed79c961361fa049bae499c33e0eb91ab22ade Mon Sep 17 00:00:00 2001
+From: Colin Walters <walt...@verbum.org>
+Date: Thu, 27 Sep 2012 21:35:22 -0400
+Subject: [PATCH 2/4] hardening: Ensure _dbus_check_setuid() is initialized
+ threadsafe manner
+
+This is a highly theoretical concern, but we might as well.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=52202
+
+Conflicts:
+	dbus/dbus-sysdeps-pthread.c
+---
+ dbus/dbus-sysdeps-pthread.c |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/dbus/dbus-sysdeps-pthread.c b/dbus/dbus-sysdeps-pthread.c
+index 46e4204..f21af85 100644
+--- a/dbus/dbus-sysdeps-pthread.c
++++ b/dbus/dbus-sysdeps-pthread.c
+@@ -358,6 +358,11 @@ check_monotonic_clock (void)
+ dbus_bool_t
+ _dbus_threads_init_platform_specific (void)
+ {
++  /* These have static variables, and we need to handle both the case
++   * where dbus_threads_init() has been called and when it hasn't;
++   * so initialize them before any threads are allowed to enter.
++   */
+   check_monotonic_clock ();
++  (void) _dbus_check_setuid ();
+   return dbus_threads_init (&pthread_functions);
+ }
+-- 
+1.7.10.4
+
diff -Nru dbus-1.2.24/debian/patches/0003-hardening-Remove-activation-helper-handling-for-DBUS.patch dbus-1.2.24/debian/patches/0003-hardening-Remove-activation-helper-handling-for-DBUS.patch
--- dbus-1.2.24/debian/patches/0003-hardening-Remove-activation-helper-handling-for-DBUS.patch	1970-01-01 01:00:00.000000000 +0100
+++ dbus-1.2.24/debian/patches/0003-hardening-Remove-activation-helper-handling-for-DBUS.patch	2012-09-29 15:52:32.000000000 +0100
@@ -0,0 +1,56 @@
+From f70d1236b0f28bbc3da53b6eacb38b2a5786bc17 Mon Sep 17 00:00:00 2001
+From: Colin Walters <walt...@verbum.org>
+Date: Fri, 28 Sep 2012 12:01:56 -0400
+Subject: [PATCH 3/4] hardening: Remove activation helper handling for
+ DBUS_VERBOSE
+
+It's not really useful.
+
+See https://bugs.freedesktop.org/show_bug.cgi?id=52202#c17
+
+Conflicts:
+	bus/activation-helper.c
+---
+ bus/activation-helper.c |   14 +-------------
+ 1 file changed, 1 insertion(+), 13 deletions(-)
+
+diff --git a/bus/activation-helper.c b/bus/activation-helper.c
+index baba8f0..bc5ed07 100644
+--- a/bus/activation-helper.c
++++ b/bus/activation-helper.c
+@@ -140,18 +140,12 @@ out_all:
+   return desktop_file;
+ }
+ 
+-/* Cleares the environment, except for DBUS_VERBOSE and DBUS_STARTER_x */
++/* Clears the environment, except for DBUS_STARTER_x */
+ static dbus_bool_t
+ clear_environment (DBusError *error)
+ {
+-  const char *debug_env = NULL;
+   const char *starter_env = NULL;
+ 
+-#ifdef DBUS_ENABLE_VERBOSE_MODE
+-  /* are we debugging */
+-  debug_env = _dbus_getenv ("DBUS_VERBOSE");
+-#endif
+-
+   /* we save the starter */
+   starter_env = _dbus_getenv ("DBUS_STARTER_ADDRESS");
+ 
+@@ -165,12 +159,6 @@ clear_environment (DBusError *error)
+     }
+ #endif
+ 
+-#ifdef DBUS_ENABLE_VERBOSE_MODE
+-  /* restore the debugging environment setting if set */
+-  if (debug_env)
+-    _dbus_setenv ("DBUS_VERBOSE", debug_env);
+-#endif
+-
+   /* restore the starter */
+   if (starter_env)
+     _dbus_setenv ("DBUS_STARTER_ADDRESS", starter_env);
+-- 
+1.7.10.4
+
diff -Nru dbus-1.2.24/debian/patches/0004-activation-helper-Ensure-DBUS_STARTER_ADDRESS-is-set.patch dbus-1.2.24/debian/patches/0004-activation-helper-Ensure-DBUS_STARTER_ADDRESS-is-set.patch
--- dbus-1.2.24/debian/patches/0004-activation-helper-Ensure-DBUS_STARTER_ADDRESS-is-set.patch	1970-01-01 01:00:00.000000000 +0100
+++ dbus-1.2.24/debian/patches/0004-activation-helper-Ensure-DBUS_STARTER_ADDRESS-is-set.patch	2012-09-29 15:52:32.000000000 +0100
@@ -0,0 +1,63 @@
+From 5b86cb6a27d4a0a6e877946ec1a666e8832614dc Mon Sep 17 00:00:00 2001
+From: Geoffrey Thomas <gtho...@mokafive.com>
+Date: Thu, 27 Sep 2012 22:02:06 -0700
+Subject: [PATCH 4/4] activation-helper: Ensure DBUS_STARTER_ADDRESS is set
+ correctly
+
+The fix for CVE-2012-3524 filters out all environment variables if
+libdbus is used from a setuid program, to prevent various spoofing
+attacks.
+
+Unfortunately, the activation helper is a setuid program linking
+libdbus, and this creates a regression for launched programs using
+DBUS_STARTER_ADDRESS, since it will no longer exist.
+
+Fix this by hardcoding the starter address to the default system bus
+address.
+
+Signed-off-by: Geoffrey Thomas <gtho...@mokafive.com>
+Signed-off-by: Colin Walters <walt...@verbum.org>
+---
+ bus/activation-helper.c |   16 +++++-----------
+ 1 file changed, 5 insertions(+), 11 deletions(-)
+
+diff --git a/bus/activation-helper.c b/bus/activation-helper.c
+index bc5ed07..bfe832e 100644
+--- a/bus/activation-helper.c
++++ b/bus/activation-helper.c
+@@ -140,15 +140,12 @@ out_all:
+   return desktop_file;
+ }
+ 
+-/* Clears the environment, except for DBUS_STARTER_x */
++/* Clears the environment, except for DBUS_STARTER_x,
++ * which we hardcode to the system bus.
++ */
+ static dbus_bool_t
+ clear_environment (DBusError *error)
+ {
+-  const char *starter_env = NULL;
+-
+-  /* we save the starter */
+-  starter_env = _dbus_getenv ("DBUS_STARTER_ADDRESS");
+-
+ #ifndef ACTIVATION_LAUNCHER_TEST
+   /* totally clear the environment */
+   if (!_dbus_clearenv ())
+@@ -159,11 +156,8 @@ clear_environment (DBusError *error)
+     }
+ #endif
+ 
+-  /* restore the starter */
+-  if (starter_env)
+-    _dbus_setenv ("DBUS_STARTER_ADDRESS", starter_env);
+-
+-  /* set the type, which must be system if we got this far */
++  /* Ensure the bus is set to system */
++  _dbus_setenv ("DBUS_STARTER_ADDRESS", DBUS_SYSTEM_BUS_DEFAULT_ADDRESS);
+   _dbus_setenv ("DBUS_STARTER_BUS_TYPE", "system");
+ 
+   return TRUE;
+-- 
+1.7.10.4
+
diff -Nru dbus-1.2.24/debian/patches/series dbus-1.2.24/debian/patches/series
--- dbus-1.2.24/debian/patches/series	2011-06-12 12:51:34.000000000 +0100
+++ dbus-1.2.24/debian/patches/series	2012-09-29 15:52:32.000000000 +0100
@@ -4,3 +4,7 @@
 11-589662-reload-kqueue.patch
 12-CVE-2010-4352-reject-deeply-nested-variants.patch
 13-629938-_dbus_header_byteswap.patch
+0001-CVE-2012-3524-Don-t-access-environment-variables-or-.patch
+0002-hardening-Ensure-_dbus_check_setuid-is-initialized-t.patch
+0003-hardening-Remove-activation-helper-handling-for-DBUS.patch
+0004-activation-helper-Ensure-DBUS_STARTER_ADDRESS-is-set.patch

Attachment: cve-2012-3524.sh
Description: application/shellscript

Reply via email to