Python interpreter binary is not compiled as PIE

Bug #1452115 reported by Jeff Dileo on 2015-05-06
270
This bug affects 4 people
Affects Status Importance Assigned to Milestone
Python
New
Unknown
python2.7 (Ubuntu)
Undecided
Unassigned
python3.4 (Ubuntu)
Undecided
Unassigned
python3.6 (Ubuntu)
Undecided
Ubuntu Security Team
python3.7 (Debian)
New
Unknown
python3.7 (Ubuntu)
Undecided
Unassigned
python3.8 (Debian)
New
Undecided
Unassigned
python3.8 (Ubuntu)
Undecided
Unassigned

Bug Description

The python2.7 binary (installed at /usr/bin/python2.7; package version 2.7.6-8) is not compiled as a position independent executable (PIE). It appears that the python compilation process is somewhat arcane and the hardening wrapper probably doesn't do the trick for it.

This is incredibly dangerous as it means that any vulnerability within a native module (e.g. ctypes-based), or within python itself will expose an incredibly large amount of known memory contents at known addresses (including a large number of dangerous instruction groupings). This enables ROP-based (https://en.wikipedia.org/wiki/Return-oriented_programming) to abuse the interpreter itself to bypass non-executable page protections.

I have put together an example vulnerable C shared object (with a buffer overflow) accessed via python through the ctypes interface as an example. This uses a single ROP "gadget" on top of using the known PLT location for system(3) (https://en.wikipedia.org/wiki/Return-to-libc_attack) to call "id". The example code is accessible at:
- https://gist.github.com/ChaosData/ae6076cb1c3cc7b0a367

I'm not exactly familiar enough with the python build process to say where exactly an -fPIE needs to be injected into a script/makefile, but I feel that given the perceived general preference for ctypes-based modules over python written ones, as the native code implementations tend to be more performant, this feels like a large security hole within the system. Given the nature of this "issue," I'm not 100% sure of where it is best reported, but from what I can tell, this conflicts with the Ubuntu hardening features and is definitely exploitable should a native module contain a sufficiently exploitable vulnerability that allows for control of the instruction register.

Jeff Dileo (jtdileo) wrote :

I should also add that python3.4 (3.4.0-2ubuntu1 ; /usr/bin/python3.4) also has the same issue. And this was only tested under 14.04 (I would assume that it also affects every other currently supported version, but don't have the space for VMs to check :( , sorry).

Sorry about the lack of that info upfront.

Changed in python2.7 (Ubuntu):
status: New → Won't Fix
Changed in python3.4 (Ubuntu):
status: New → Confirmed
Changed in python2.7 (Ubuntu):
status: Won't Fix → Confirmed
information type: Private Security → Public Security
Seth Arnold (seth-arnold) wrote :

We didn't enable PIE for the python interpreters for performance reasons.

We're currently investigating turning PIE on by default for x86-64 and
other architectures that will likely handle it well. The performance
impact will be one of the deciding factors in determining if we enable
PIE for the python interpreter.

We're not likely to backport the PIE support to existing releases
because newer versions of GCC help reduce the performance penalty of
PIE compilation.

Thanks

It's been 2 years, can we turn on PIE for Python now?

Alpine and other distros do this by default.

Matthias Klose (doko) wrote :

this is done since 16.10. See the release notes

Changed in python2.7 (Ubuntu):
status: Confirmed → Fix Released
Changed in python3.4 (Ubuntu):
status: Confirmed → Fix Released
Paolo Pettinato (p.pettinato) wrote :

I do believe pie is explicitly disabled when building Python 3.6. Using hardening-check on Ubuntu Bionic (from the devscripts package):

$ hardening-check /usr/bin/python3
/usr/bin/python3:
 Position Independent Executable: no, normal executable!
 Stack protected: yes
 Fortify Source functions: yes (some protected functions found)
 Read-only relocations: yes
 Immediate binding: no, not found!

Also from debian/rules in http://archive.ubuntu.com/ubuntu/pool/main/p/python3.6/python3.6_3.6.5-3.debian.tar.xz:

export DEB_BUILD_MAINT_OPTIONS=hardening=-pie

According to http://manpages.ubuntu.com/manpages/bionic/man1/dpkg-buildflags.1.html this syntax disables pie - it should be "+pie", and is enabled by default on Bionic:

$ dpkg-buildflags --status
dpkg-buildflags: status: vendor is Ubuntu
dpkg-buildflags: status: future features: lfs=no
dpkg-buildflags: status: hardening features: bindnow=no format=yes fortify=yes pie=yes relro=yes stackprotector=yes stackprotectorstrong=yes
dpkg-buildflags: status: qa features: bug=no canary=no
dpkg-buildflags: status: reproducible features: fixdebugpath=yes timeless=yes
dpkg-buildflags: status: sanitize features: address=no leak=no thread=no undefined=no
...

with the environment variable set:
$ DEB_BUILD_MAINT_OPTIONS=hardening=-pie dpkg-buildflags --status
dpkg-buildflags: status: environment variable DEB_BUILD_MAINT_OPTIONS=hardening=-pie
dpkg-buildflags: status: vendor is Ubuntu
dpkg-buildflags: status: future features: lfs=no
dpkg-buildflags: status: hardening features: bindnow=no format=yes fortify=yes pie=no relro=yes stackprotector=yes stackprotectorstrong=yes
dpkg-buildflags: status: qa features: bug=no canary=no
dpkg-buildflags: status: reproducible features: fixdebugpath=yes timeless=yes
dpkg-buildflags: status: sanitize features: address=no leak=no thread=no undefined=no
...

Giovanni Pellerano (evilaliv3) wrote :

Actually I confirm this on current ubuntu bionic.

Would someone please reach the ubuntu security team and verify this is an intended choice?

evilaliv3@evilaliv3:~$ hardening-check /usr/bin/python3
/usr/bin/python3:
 Position Independent Executable: no, normal executable!
 Stack protected: yes
 Fortify Source functions: yes (some protected functions found)
 Read-only relocations: yes
 Immediate binding: no, not found!

hardening-check /usr/bin/python2
/usr/bin/python2:
 Position Independent Executable: yes
 Stack protected: yes
 Fortify Source functions: yes (some protected functions found)
 Read-only relocations: yes
 Immediate binding: yes

Changed in python3.6 (Ubuntu):
assignee: nobody → Ubuntu Security Team (ubuntu-security)
Changed in python:
status: Unknown → New
Launchpad Janitor (janitor) wrote :

Status changed to 'Confirmed' because the bug affects multiple users.

Changed in python3.6 (Ubuntu):
status: New → Confirmed
Changed in python3.8 (Ubuntu):
status: New → Confirmed
Jarek Zgoda (jarek-zgoda) wrote :

3.7 is also affected in bionic:

$ hardening-check /usr/bin/python3.7
/usr/bin/python3.7:
 Position Independent Executable: no, normal executable!
 Stack protected: yes
 Fortify Source functions: yes (some protected functions found)
 Read-only relocations: yes
 Immediate binding: no, not found!

ddylihfq (ddylihfq) wrote :

Relocation Read-Only(RELRO) also only partially implemented in python 3.6 compared to 2.7, as well as missing PIE on Bionic:

FILE: /usr/bin/python3.6
RELRO: Partial RELRO <<< ISSUE >>>
STACK CANARY: Canary found
NX: NX enabled
PIE: No PIE <<< ISSUE >>>
RPATH: No RPATH
RUNPATH: No RUNPATH
Symbols: No Symbols
FORTIFY: Yes
Fortified: 18
Fortifiable: 42

FILE: /usr/bin/python2.7
RELRO: Full RELRO
STACK CANARY: Canary found
NX: NX enabled
PIE: PIE enabled <<<
RPATH: No RPATH
RUNPATH: No RUNPATH
Symbols: No Symbols
FORTIFY: Yes
Fortified: 14
Fortifiable: 32

Steve Beattie (sbeattie) on 2020-07-14
Changed in python3.7 (Ubuntu):
status: New → Confirmed
Giovanni Pellerano (evilaliv3) wrote :

Hello! Does anyone really care?

5 years passed since the original reporting of this issue and i'm starting to seriously think that this intended to cover up some zer0 day!

Many were the justification to this related to performance but actually with many tests this appeared to not be the case: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=919134

Jeff Dileo (jtdileo) wrote :

@Giovanni Pellerano (evilaliv3): So while lack of any of these (currently mainstream) hardening features is concerning with regards to exploitation (especially the lack of ASLR in a generally non-highly interactive exploitation context), my guess is that the upstream Python build toolchain is just outmoded and buggy and package maintainers are left holding the bag. Hanlon's razor.

Changed in python3.7 (Debian):
status: Unknown → New
To post a comment you must log in.
This report contains Public Security information  Edit
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.