=== modified file 'NEWS' --- NEWS 2015-10-19 06:46:20 +0000 +++ NEWS 2015-10-19 07:48:02 +0000 @@ -3,6 +3,11 @@ 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-XXXX, 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 === modified file 'apport/report.py' --- apport/report.py 2015-09-01 05:50:54 +0000 +++ apport/report.py 2015-10-19 07:41:49 +0000 @@ -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('/', '.') - - try: - m = __import__(module) - m - except: - return None - - # 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__ - - if path.endswith('.pyc'): + module = module.replace('/', '.').split('.') + pathlist = sys.path + + path = None + while module: + name = module.pop(0) + + 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 and path.endswith('.pyc'): path = path[:-1] return path === modified file 'test/test_report.py' --- test/test_report.py 2015-02-10 10:21:40 +0000 +++ test/test_report.py 2015-10-19 07:28:46 +0000 @@ -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'''