diff -u apport-2.0.1/apport/report.py apport-2.0.1/apport/report.py --- apport-2.0.1/apport/report.py +++ apport-2.0.1/apport/report.py @@ -10,7 +10,7 @@ # the full text of the license. import subprocess, tempfile, os.path, urllib, re, pwd, grp, os -import fnmatch, glob, traceback, errno +import fnmatch, glob, traceback, errno, sys, imp import xml.dom, xml.dom.minidom from xml.parsers.expat import ExpatError @@ -380,15 +380,26 @@ def _python_module_path(klass, module): '''Determine path of given Python module''' - try: - m = __import__(module.replace('/', '.')) - m - except: - return None + module = module.replace('/', '.').split('.') + pathlist = sys.path - # chop off the first component, as it's already covered by m - path = eval('m.%s.__file__' % '.'.join(module.split('/')[1:])) - if path.endswith('.pyc'): + 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 diff -u apport-2.0.1/debian/changelog apport-2.0.1/debian/changelog --- apport-2.0.1/debian/changelog +++ apport-2.0.1/debian/changelog @@ -1,3 +1,13 @@ +apport (2.0.1-0ubuntu17.12) precise-security; urgency=medium + + * 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 15:50:47 +0200 + apport (2.0.1-0ubuntu17.11) precise-proposed; urgency=medium * report.py, add_package_info(): Add "[origin: unknown]" tag to diff -u apport-2.0.1/test/test_report.py apport-2.0.1/test/test_report.py --- apport-2.0.1/test/test_report.py +++ apport-2.0.1/test/test_report.py @@ -1,5 +1,5 @@ # coding: UTF-8 -import unittest, shutil, time, tempfile, os, subprocess, grp, atexit, re +import unittest, shutil, time, tempfile, os, subprocess, grp, atexit, re, sys import apport.report import problem_report @@ -386,6 +386,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) + def test_check_interpreted_twistd(self): '''_check_interpreted() for programs ran through twistd'''