O mais interessante do projeto PaX é a pesquisa de vários mecanismos
para proteger o sistema de explorações que dêem ao atacante
privilégios para ler/escrever em determinados segmentos da memória
do address space.
Essas classes de falhas são as mais exploradas por atacantes que
visam elevar seu nível de atuação frente ao sistema, obtendo
privilégios de root (uid=0), portanto o PaX vai ajudar e muito na
prevenção de tais explorações, impedindo assim execuções arbitrárias
de códigos para alocação de memória não permitida no sistema.
Estamos frisando aqui que o que iremos fazer não é analisar e corrigir
bugs no sistema, mas sim torná-los o menos acessíveis possível. O PaX
para isso utiliza três níveis distintos para determinadas técnicas de exploração:
- Introdução e execução de códigos arbitrários.
- Execução de códigos fora da ordem original do programa.
- Execução de códigos dentro da ordem original do programa usando dados arbitrários.
Isso já seria suficiente para que um shellcode conhecido (1) fosse impedido de
rodar em uma tentativa de ataque de return-into-libc (2), o que já seria uma grande
implementação, pois a técnica de retlibc e uma das mais avançadas em questão de overflows.
Uma das principais modificações que o PaX irá causar em nosso sistema como poderíamos
ver é a randomização do nosso buffer de saída, o return address.
Poderíamos verificar isso com um simples script que nos retorne o valor do buffer:
$ cc teste_mmap.c -o teste_mmap
$ ./teste_mmap
0xbfff61ec
$ ./teste_mmap
0xbff9073c
$ ./teste_mmap
0xbffe7f6c
$ ./teste_mmap
0xbff9b85c
Se você quiser fazer o teste, verá que se rodar teste script em um
kernel sem o PaX implementado, ele retornará sempre o mesmo valor para o buffer.
Em que isso me ajudará, bom se você conhece técnicas como Stack ou Heap
overflow sabe que para conseguir sobrescrever o return address você deve saber
seu endereço na memória que este aloca, portanto com um debug no programa alvo
você conseguiria o break point dele. Com o PaX implementado esse endereço se
tornará arbitrário e apenas o próprio programa que alocou a memória (com as
devidas permissões que podem ser setadas pelo PaX) poderá controlar seu fluxo
de execução.
Mais à frente no capítulo "Testes" demonstrarei como o PaX reage a
determinadas tentativas de exploração.
Um pouco sobre as principais funções do PaX
Vamos tratar das principais funções que o PaX utiliza para proteger nosso sistema:
- RandStack
- Randmmap
- Randexec
[+] Non-Executable Pages
O NONEXEC tem sua principal vantagem em prevenir a inserção e execução de
códigos no
address space do sistema, tornando assim qualquer tipo
de técnica de exploração que use essa metodologia ineficaz.
Existem dois meios de se introduzir um código arbitrário e um
address space:
um executável para mapeamento ou modificando um executável com permissão de escrita/execução
para mapeamento.
- O primeiro método não é segurado pelo PaX porque existem muitos métodos de
controle para esse tipo de acesso.
- O segundo método pode ser prevenido se for controlado a criação de executáveis de
mapeamento com permissão para escrita/execução que executem alguma outra chamada
system() diretamente em nosso sistema.
Com isso estaremos eliminando muitas possibilidades de comprometimento.
O último ponto que podemos analisar sobre o NONEXEC é que ele trava o acesso a memória
semântica do sistema. Assim ele proíbe qualquer programa não autorizado de alocar memória
para rodar em seu sistema. Iremos fazer alguns testes mostrando como ele pode coibir a
ação de um exploit para explorar um Stack Overflow.
[+] RandStack
A função do Randstack e introduzir a randomização na kernel para os endereços de returno
das Stacks.
O
Linux libera em geral duas pages do
kernel stack para cada chamada realizada. Esses
stacks são utilizados sempre que as chamadas vão para o kernel (system call, device
interrupt, CPU exception entre outros).
Todo caso quando a stack volta para o
user space ela tem um valor de retorno,
o conhecido ESP (Executable Stack Pointer). No caso esse seria o ponto que deve ser
sobrescrito para que uma determinada função seja introduzida no fluxo de um determinado
programa. A randomização faz com que esse ponto não consiga ser calculado com exatidão
deixando o stack de maneira assíncrona.
[+] Randmmap
O principal atrativo do RANDMMAP é a introdução de randomização em regiões de memória
utilizadas pelas interfaces
mmap() do kernel. Isso inclui também todos os arquivos
e mapeamentos anônimos do sistema, tais como os principais executáveis, libraries, que
tem seu
heap gerenciado por funções como
brk() e
mmap().
Você pode encontrar muito mais na integra de todas as funções do PaX diretamente no endereço: