Como testar se um binário do Linux foi compilado como código independente de posição?

31

Recentemente aprendi que (pelo menos no Fedora e no Red Hat Enterprise Linux), programas executáveis que são compilados como Position Independent Executables (PIE) recebem proteção mais strong de randomização de espaço de endereço (ASLR).

Então: Como eu testo se um executável específico foi compilado como um Executável Independente de Posição, no Linux?

    
por D.W. 03.09.2013 / 07:50

5 respostas

30

Você pode usar o script perl contido no pacote hardening-check , disponível no Fedora e Debian (como hardening-includes ). Leia esta página wiki Debian para detalhes sobre quais flags de compilação são verificados. É específico do Debian, mas a teoria também se aplica à Red Hat.

Exemplo:

$ 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
    
por 03.09.2013 / 08:26
13

Eu usei readelf --relocs para testar se a biblioteca estática ou dinâmica é PIC no x86-64 da seguinte maneira:

$ 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

Nós vemos aqui R_X86_64_32 e R_X86_64_32S . Isso significa que o código não é independente da posição. Quando eu reconstruo uma biblioteca com -fPIC eu recebo:

$ 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

Este método provavelmente funciona para executáveis, mas eu não usei dessa forma.

    
por 03.09.2013 / 09:30
9

Basta usar file no binário:

$ 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

Observe o tipo diferente impresso após as informações do LSB.

    
por 18.11.2016 / 15:10
4

GDB executa o executável duas vezes

OK, essa não é a possibilidade mais prática em muitos casos, mas é educacional e satisfaz minha obsessão por ver as coisas realmente funcionando.

printf '
#include <stdio.h>
int main() {
    puts("hello world");
}
' > main.c
gcc -std=c99 -pie -fpie -ggdb3 -o pie main.c
gcc -std=c99 -no-pie -fno-pie -ggdb3 -o nopie main.c
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
gdb -batch -nh -ex 'set disable-randomization off' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  ./pie
gdb -batch -nh -ex 'set disable-randomization off' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  ./nopie

Para aquele com -pie , vemos que o endereço de main muda entre as execuções:

Temporary breakpoint 1 at 0x7a9: file memory_layout.c, line 31.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main (argc=1, argv=0x7ffe57d75318) at memory_layout.c:31
31      int main(int argc, char **argv) {
Line 31 of "memory_layout.c" starts at address 0x55db0066b79a <main> and ends at 0x55db0066b7a9 <main+15>.
Temporary breakpoint 2 at 0x55db0066b7a9: file memory_layout.c, line 31.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 2, main (argc=1, argv=0x7ffd03b67d68) at memory_layout.c:31
31      int main(int argc, char **argv) {
Line 31 of "memory_layout.c" starts at address 0x563910ccd79a <main> and ends at 0x563910ccd7a9 <main+15>.

Neste exemplo, o endereço da primeira execução foi 0x55db0066b79a e o segundo 0x563910ccd79a .

Mas, para o com -no-pie , o endereço de main permanece o mesmo 0x400627 para as duas execuções:

Temporary breakpoint 1 at 0x400636: file ./memory_layout.c, line 28.

Temporary breakpoint 1, main (argc=1, argv=0x7ffd5f69c8b8) at ./memory_layout.c:28
warning: Source file is more recent than executable.
28      int bss = 0;
Line 28 of "./memory_layout.c" starts at address 0x400627 <main> and ends at 0x400636 <main+15>.
Temporary breakpoint 2 at 0x400636: file ./memory_layout.c, line 28.

Temporary breakpoint 2, main (argc=1, argv=0x7ffdd9f74bd8) at ./memory_layout.c:28
28      int bss = 0;
Line 28 of "./memory_layout.c" starts at address 0x400627 <main> and ends at 0x400636 <main+15>.

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space garante que o ASLR esteja ativado (o padrão no Ubuntu 17.10): Como posso desativar temporariamente o ASLR (randomização de layout de espaço de endereço)? | Pergunte ao Ubuntu .

set disable-randomization off é necessário caso contrário, o GDB, como o nome sugere, desativa o ASLR para o processo, por padrão, para fornecer endereços fixos em execuções para melhorar a experiência de depuração: Diferença entre endereços gdb e endereços" reais "? | Estouro de pilha

readelf diversão

Além disso, também podemos observar que:

readelf -s ./nopie | grep main

fornece o endereço de carregamento real do tempo de execução:

69: 0000000000400627   370 FUNC    GLOBAL DEFAULT   13 main

enquanto:

readelf -s ./pie | grep main

dá apenas um deslocamento:

70: 000000000000079a   401 FUNC    GLOBAL DEFAULT   14 main

Ao desativar o ASLR (com randomize_va_space ou set disable-randomization off ), o GDB sempre fornece main o endereço: 0x5555555547a9 , então deduzimos que o endereço -pie é composto de:

0x555555554000 + random offset + symbol offset (79a)

TODO onde está 0x555555554000 codificado no kernel do Linux / glibc loader / where?

Testado no Ubuntu 18.04.

Pergunta relacionada:

    
por 02.04.2018 / 14:07
1

Existe um script bash checksec.sh no Github para verificar as propriedades de mitigação de executáveis (incluindo RELRO, Stack Canary, NX bit, PIE, RPATH, RUNPATH, Fortify Source).

Execute checksec com -f (entrada de arquivo) argumentos:

$ 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
    
por 08.05.2018 / 14:29