diff -u python2.7-2.7.6/debian/changelog python2.7-2.7.6/debian/changelog --- python2.7-2.7.6/debian/changelog +++ python2.7-2.7.6/debian/changelog @@ -1,3 +1,11 @@ +python2.7 (2.7.6-8ubuntu0.4) trusty-security; urgency=medium + + * SECURITY UPDATE: find_library shell execution + - debian/patches/fix_ctypes_util_shell_exec_vuln.patch: use + subprocess.Popen instead of os.popen in Lib/ctypes/util.py + + -- Brian Morton Tue, 13 Dec 2016 15:48:21 -0500 + python2.7 (2.7.6-8ubuntu0.3) trusty-security; urgency=medium * SECURITY UPDATE: StartTLS stripping attack diff -u python2.7-2.7.6/debian/patches/series.in python2.7-2.7.6/debian/patches/series.in --- python2.7-2.7.6/debian/patches/series.in +++ python2.7-2.7.6/debian/patches/series.in @@ -81,0 +82 @@ +fix-ctypes-util-shell-exec-vuln.patch only in patch2: unchanged: --- python2.7-2.7.6.orig/debian/patches/fix-ctypes-util-shell-exec-vuln.patch +++ python2.7-2.7.6/debian/patches/fix-ctypes-util-shell-exec-vuln.patch @@ -0,0 +1,124 @@ +Description: Fix shell execution in find_library + The find_library function in Python 2.7 allows + arbitrary command execution. +Origin: upstream, http://bugs.python.org/file43387/ctypes_util_popen-6.py2.patch +Bug: http://bugs.python.org/issue22636 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python2.7/+bug/1512068 +Forwarded: not-needed +Author: Brian Morton +Last-Update: 2016-12-13 + +--- a/Lib/ctypes/test/test_find.py ++++ b/Lib/ctypes/test/test_find.py +@@ -1,5 +1,7 @@ + import unittest ++import os, os.path + import sys ++from test import test_support + from ctypes import * + from ctypes.util import find_library + from ctypes.test import is_resource_enabled +@@ -55,6 +57,11 @@ + if self.gle: + self.gle.gleGetJoinStyle + ++def test_shell_injection(self): ++ result = find_library('; echo Hello shell > ' + test_support.TESTFN) ++ self.assertFalse(os.path.lexists(test_support.TESTFN)) ++ self.assertIsNone(result) ++ + ##if os.name == "posix" and sys.platform != "darwin": + + ## # On platforms where the default shared library suffix is '.so', +--- a/Lib/ctypes/util.py ++++ b/Lib/ctypes/util.py +@@ -1,7 +1,9 @@ + ###################################################################### + # This file should be kept compatible with Python 2.3, see PEP 291. # + ###################################################################### +-import sys, os ++import os ++import subprocess ++import sys + + # find_library(name) returns the pathname of a library, or None. + if os.name == "nt": +@@ -90,24 +92,20 @@ + + def _findLib_gcc(name): + expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name) +- fdout, ccout = tempfile.mkstemp() +- os.close(fdout) +- cmd = 'if type gcc >/dev/null 2>&1; then CC=gcc; elif type cc >/dev/null 2>&1; then CC=cc;else exit 10; fi;' \ +- 'LANG=C LC_ALL=C $CC -Wl,-t -o ' + ccout + ' 2>&1 -l' + name ++ cmd = 'if type gcc >/dev/null 2>&1; then CC=gcc; elif type cc >/dev/null 2>&1; then CC=cc;else exit; fi;' \ ++ 'LANG=C LC_ALL=C $CC -Wl,-t -o "$2" 2>&1 -l"$1"' ++ temp = tempfile.NamedTemporaryFile() + try: +- f = os.popen(cmd) +- try: +- trace = f.read() +- finally: +- rv = f.close() ++ proc = subprocess.Popen((cmd, '_findLib_gcc', name, temp.name), ++ shell=True, ++ stdout=subprocess.PIPE) ++ [trace, _] = proc.communicate() + finally: + try: +- os.unlink(ccout) ++ temp.close() + except OSError, e: + if e.errno != errno.ENOENT: + raise +- if rv == 10: +- raise OSError, 'gcc or cc command not found' + res = re.search(expr, trace) + if not res: + return None +@@ -134,19 +132,12 @@ + # assuming GNU binutils / ELF + if not f: + return None +- cmd = 'if ! type objdump >/dev/null 2>&1; then exit 10; fi;' \ +- "objdump -p -j .dynamic 2>/dev/null " + f +- f = os.popen(cmd) +- dump = f.read() +- rv = f.close() +- if rv == 10: +- raise OSError, 'objdump command not found' +- f = os.popen(cmd) +- try: +- data = f.read() +- finally: +- f.close() +- res = re.search(r'\sSONAME\s+([^\s]+)', data) ++ cmd = 'if ! type objdump >/dev/null 2>&1; then exit; fi;' \ ++ 'objdump -p -j .dynamic 2>/dev/null "$1"' ++ proc = subprocess.Popen((cmd, '_get_soname', f), shell=True, ++ stdout=subprocess.PIPE) ++ [dump, _] = proc.communicate() ++ res = re.search(br'\sSONAME\s+([^\s]+)', dump) + if not res: + return None + return res.group(1) +@@ -239,11 +230,15 @@ + + # XXX assuming GLIBC's ldconfig (with option -p) + expr = r'\s+(lib%s\.[^\s]+)\s+\(%s' % (re.escape(name), abi_type) +- f = os.popen('/sbin/ldconfig -p 2>/dev/null') ++ null = open(os.devnull, 'wb') + try: +- data = f.read() +- finally: +- f.close() ++ with null: ++ p = subprocess.Popen(['/sbin/ldconfig', '-p'], ++ stderr=null, ++ stdout=subprocess.PIPE) ++ except OSError: # E.g. command not found ++ return None ++ [data, _] = p.communicate() + res = re.search(expr, data) + if not res: + return None