35

I recently learned that (at least on Fedora and Red Hat Enterprise Linux), executable programs that are compiled as Position Independent Executables (PIE) receive stronger address space randomization (ASLR) protection.

So: How do I test whether a particular executable was compiled as a Position Independent Executable, on Linux?

migrated from serverfault.com Sep 3 '13 at 6:12

This question came from our site for system and network administrators.

  • 1
    Not sure about 32-bit, but on x86_64 code is position independent by default. And of course all the system packages are compiled this way on either arch. – Michael Hampton Sep 3 '13 at 6:12
  • 1
    @MichaelHampton, I don't think that's right. (Be careful about the difference between an executable binary and a shared library; your statement might be right for shared libraries, but I don't think it's right for executables.) Even on x86_64, binaries do not appear to be PIE by default. I just wrote a small test program, and on x86_64, it was not compiled as PIE. I think you have to pass the -pie -fpie special compiler flags to compile a program as a PIE. That link had other interesting information, though -- thank you! – D.W. Sep 3 '13 at 7:23
  • 1
    This guy has a bash script for detection: blog.fpmurphy.com/2008/06/position-independent-executables.html – CMCDragonkai Apr 15 '16 at 16:28
30

You can use the perl script contained in the hardening-check package, available in Fedora and Debian (as hardening-includes). Read this Debian wiki page for details on what compile flags are checked. It's Debian specific, but the theory applies to Red Hat as well.

Example:

$ hardening-check $(which sshd)
/usr/sbin/sshd:
 Position Independent Executable: yes
 Stack protected: yes
 Fortify Source functions: yes (some protected functions found)
 Read-only relocations: yes
 Immediate binding: yes
  • Nice answer, also applicable to Ubuntu 16.04 LTS and possibly other Ubuntu versions. sudo apt-get install hardening-includes and then the hardening-check executable perl script is available on the usual PATH(/usr/bin/hardening-check); just a nit: Suggest to remove the ./ from the answer ;-) – Dilettant Feb 1 '17 at 12:06
  • @a25bedc5-3d09-41b8-82fb-ea6c353d75ae not in 17.10 anymore :-( – Ciro Santilli 新疆改造中心996ICU六四事件 Oct 26 '17 at 5:38
  • In CentOS/RedHat, this package is available in epel repository – vikas027 Feb 21 '18 at 0:35
  • @a25bedc5-3d09-41b8-82fb-ea6c353d75ae Looks like it is no longer available in Ubuntu 18.04 – Vadim Kotov Jun 28 '18 at 13:08
  • The debian package that contains this is now called devscripts. – Tamás Szelei Nov 9 '18 at 22:13
14

I used readelf --relocs to test whether static or dynamic library is PIC on x86-64 the following way:

$ readelf --relocs /usr/lib/gcc/x86_64-linux-gnu/4.6/libstdc++.a |\
      awk '$3~/^R_/ && $5!~/^\.debug/{print $3}' |sort -u
R_X86_64_32
R_X86_64_32S
R_X86_64_64
R_X86_64_DTPOFF32
R_X86_64_GOTPCREL
R_X86_64_PC32
R_X86_64_PLT32
R_X86_64_TLSLD
R_X86_64_TPOFF32

We see here R_X86_64_32 and R_X86_64_32S. This means that the code is not position independent. When I rebuild a library with -fPIC I get:

$ readelf --relocs libstdc++.a |\
      awk '$3~/^R_/ && $5!~/^\.debug/{print $3}' |sort -u
R_X86_64_64
R_X86_64_DTPOFF32
R_X86_64_GOTPCREL
R_X86_64_PC32
R_X86_64_PLT32
R_X86_64_TLSGD
R_X86_64_TLSLD

This method may probably work for executables, but I have not used it that way.

  • 8
    Would you care to explain how to interpret the output of that one-liner? What's the criteria to use to classify the shared library as PIC vs non-PIC? – D.W. Sep 3 '13 at 18:16
  • If you built an executable with -fPIE -no-pie, it would always be loaded at the same address even though it could have been linked as a PIE executable. Use file a.out and look for ELF executable (non-PIE) vs. ELF shared object` (PIE): 32-bit absolute addresses no longer allowed in x86-64 Linux? – Peter Cordes May 16 '18 at 2:54
11

Simply use file on the binary:

$ file ./pie-off
./pie-off: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=0dc3858e9f0334060bfebcbe3e854909191d8bdc, not stripped
$ file ./pie-on
./pie-on: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=962235df5bd188e1ec48c151ff61b6435d395f89, not stripped

Note the different type printed after the LSB information.

  • 1
    How does this show if compiled with PIE/ASLR? – Baruch Apr 26 '17 at 19:15
  • 3
    The only difference between the outputs from pie-off and pie.on are executable and shared object. I presume shared objects need to be relocatable hence to my mind have been compiled with PIE. – Richard Braganza Jul 9 '17 at 19:11
  • Yup, PIE executables are ELF shared objects; the easiest way to implement ASLR for executables was to use the existing support in the dynamic linker for and ELF entry point in a shared object. See also 32-bit absolute addresses no longer allowed in x86-64 Linux? for more about gcc options that control PIE, and that gcc -fPIE -pie is now the default on many distros. – Peter Cordes May 16 '18 at 2:57
  • Newer versions of file explicitly mention pie: e.g. ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=9b502fd78165cb04aec34c3f046c1ba808365a96, stripped – Brian Minton Jan 24 at 17:40
  • 1
    @PeterCordes note that file 5.36 now can actually recognize PIE-ness based on the DT_1_PIE flag of DT_FLAGS_1, and clearly says pie executable instead of shared object. – Ciro Santilli 新疆改造中心996ICU六四事件 Apr 16 at 9:46
4

file 5.36 says it clearly

file 5.36 actually prints it clearly if the executable is PIE or not. For example, a PIE executable shows as:

main.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, not stripped

and a non-PIE one as:

main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

The feature was introduced in 5.33 but it did just a simple chmod +x check. Before that it just printed shared object for PIE.

In 5.34, it was meant to start checking the more specialized DF_1_PIE ELF metadata, but due to a bug in the implementation it actually broke things and showed GCC PIE executables as shared objects.

I have interpreted file source code, including the bug, and exactly which bytes of the ELF format it checks in excruciating detail at: https://stackoverflow.com/questions/34519521/why-does-gcc-create-a-shared-object-instead-of-an-executable-binary-according-to/55704865#55704865

A quick summary of file 5.36 behavior is:

  • if Elf32_Ehdr.e_type == ET_EXEC
    • print executable
  • else if Elf32_Ehdr.e_type == ET_DYN
    • if DT_FLAGS_1 dynamic section entry is present
      • if DF_1_PIE is set in DT_FLAGS_1:
        • print pie executable
      • else
        • print shared object
    • else
      • if file is executable by user, group or others
        • print pie executable
      • else
        • print shared object

GDB run the executable twice and see ASLR

One very direct thing that you can do is to run the executable twice through GDB and see if the address changes across runs due to ASLR.

I have explained how to do that in detail at: https://stackoverflow.com/questions/2463150/what-is-the-fpie-option-for-position-independent-executables-in-gcc-and-ld/51308031#51308031

While this is not necessarily the most practical solution and not possible if you don't trust the executable, it is fun and it does the ultimate check that we really care about, which is if the Linux kernel / dynamic loader changes the executable location or not.

  • 1
    "address of main changes between runs" - this is not effect of pure PIE, it is PIE and enabled ASLR. Yes, it is almost anywhere enabled, but for machines with disabled ASLR address will be the same both times. ASLR may be enabled globally but disabled with setarch -R man7.org/linux/man-pages/man8/setarch.8.html "-R, --addr-no-randomize Disables randomization of the virtual address space. Turns on ADDR_NO_RANDOMIZE." man7.org/linux/man-pages/man2/personality.2.html "ADDR_NO_RANDOMIZE (since Linux 2.6.12) With this flag set, disable address-space-layout randomization." – osgx Jul 14 '18 at 21:46
1

There is bash script checksec.sh on Github to check the executables mitigation properties (including RELRO, Stack Canary, NX bit, PIE, RPATH, RUNPATH, Fortify Source).

Run checksec with -f (file input) arguments:

$ checksec -f /usr/bin/bash

RELRO           STACK CANARY      NX            PIE             RPATH     RUNPATH      FORTIFY Fortified Fortifiable
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH    YES      13        33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.