diff -u apport-2.19.1/NEWS apport-2.19.1/NEWS --- apport-2.19.1/NEWS +++ apport-2.19.1/NEWS @@ -3,11 +3,18 @@ 2.19.2 (UNRELEASED) ------------------- + * SECURITY FIX: When determining the path of a Python module for a program + like "python -m module_name", avoid actually importing and running the + module; this could lead to local root privilege escalation. Thanks to + Gabriel Campana for discovering this and the fix! + (CVE-2015-1341, LP: #1507480) * apt/dpkg: Don't mark packages downloaded from Launchpad for installation by apt. Thanks Brian Murray. * Fix backend_apt_dpkg.test_install_packages_system for recent "Fall back to direct Launchpad ddeb download" fix. coreutils-dbgsym should now always be available independent of whether the local system has ddeb apt sources. + * test_backend_apt_dpkg.py: Reset internal apt caches between tests. Avoids + random test failures due to leaking paths from previous test cases. 2.19.1 (2015-10-07) ------------------- diff -u apport-2.19.1/debian/changelog apport-2.19.1/debian/changelog --- apport-2.19.1/debian/changelog +++ apport-2.19.1/debian/changelog @@ -1,3 +1,15 @@ +apport (2.19.1-0ubuntu4) wily-security; urgency=medium + + * test_backend_apt_dpkg.py: Reset internal apt caches between tests. Avoids + random test failures due to leaking paths from previous test cases. + * SECURITY FIX: When determining the path of a Python module for a program + like "python -m module_name", avoid actually importing and running the + module; this could lead to local root privilege escalation. Thanks to + Gabriel Campana for discovering this and the fix! + (CVE-2015-1341, LP: #1507480) + + -- Martin Pitt Thu, 22 Oct 2015 14:46:22 +0200 + apport (2.19.1-0ubuntu3) wily; urgency=medium * Disable Launchpad crash upload for final Ubuntu 15.10. diff -u apport-2.19.1/test/test_backend_apt_dpkg.py apport-2.19.1/test/test_backend_apt_dpkg.py --- apport-2.19.1/test/test_backend_apt_dpkg.py +++ apport-2.19.1/test/test_backend_apt_dpkg.py @@ -46,6 +46,9 @@ # save and restore configuration file self.orig_conf = impl.configuration self.workdir = tempfile.mkdtemp() + # reset internal caches between tests + impl._apt_cache = None + impl._sandbox_apt_cache = None def tearDown(self): impl.configuration = self.orig_conf only in patch2: unchanged: --- apport-2.19.1.orig/apport/report.py +++ apport-2.19.1/apport/report.py @@ -10,7 +10,7 @@ # the full text of the license. import subprocess, tempfile, os.path, re, pwd, grp, os, time -import fnmatch, glob, traceback, errno, sys, atexit, locale +import fnmatch, glob, traceback, errno, sys, atexit, locale, imp import xml.dom, xml.dom.minidom from xml.parsers.expat import ExpatError @@ -463,22 +463,26 @@ def _python_module_path(klass, module): '''Determine path of given Python module''' - module = module.replace('/', '.') + module = module.replace('/', '.').split('.') + pathlist = sys.path - try: - m = __import__(module) - m - except: - return None + path = None + while module: + name = module.pop(0) - # chop off the first component, as it's already covered by m - submodule = module.split('.')[1:] - if submodule: - path = eval('m.%s.__file__' % '.'.join(submodule)) - else: - path = m.__file__ + try: + (fd, path, desc) = imp.find_module(name, pathlist) + except ImportError: + path = None + break + if fd: + fd.close() + pathlist = [path] + + if not module and desc[2] == imp.PKG_DIRECTORY: + module = ['__init__'] - if path.endswith('.pyc'): + if path and path.endswith('.pyc'): path = path[:-1] return path only in patch2: unchanged: --- apport-2.19.1.orig/test/test_report.py +++ apport-2.19.1/test/test_report.py @@ -453,6 +453,25 @@ if restore_root: os.setresuid(0, 0, -1) + def test_check_interpreted_no_exec(self): + '''_check_interpreted() does not run module code''' + + # python script through -m, with dot separator; top-level module + pr = apport.report.Report() + pr['ExecutablePath'] = '/usr/bin/python' + pr['ProcStatus'] = 'Name:\tpython' + pr['ProcCmdline'] = 'python\0-m\0unittest.__main__' + orig_argv = sys.argv + try: + sys.argv = ['/usr/bin/python', '-m', 'unittest.__main__'] + pr._check_interpreted() + finally: + sys.argv = orig_argv + self.assertTrue(pr['ExecutablePath'].endswith('unittest/__main__.py'), + pr['ExecutablePath']) + self.assertEqual(pr['InterpreterPath'], '/usr/bin/python') + self.assertNotIn('UnreportableReason', pr) + @unittest.skipUnless(have_twistd, 'twisted is not installed') def test_check_interpreted_twistd(self): '''_check_interpreted() for programs ran through twistd'''