<summary><strong>Aprenda hacking na AWS do zero ao herói com</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* 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)!
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe seus truques de hacking enviando PRs para os** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
Para verificar se as chamadas de sistema estão sendo feitas corretamente, compile o programa anterior e as chamadas de sistema devem aparecer em **strace ./PROGRAMA\_COMPILADO**
Ao criar shellcodes, um truque pode ser realizado. A primeira instrução é um salto para uma chamada. A chamada chama o código original e também coloca o EIP na pilha. Após a instrução de chamada, inserimos a string necessária, para que com esse EIP possamos apontar para a string e continuar executando o código.
Consiste em um pequeno código que percorre as páginas de memória associadas a um processo em busca da shellcode ali armazenada (procura por alguma assinatura na shellcode). Útil nos casos em que há pouco espaço disponível para injetar código.
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**:
Útil quando não é possível inserir um endereço de stack no EIP (verifica-se que o EIP não contém 0xbf) ou quando não é possível calcular a localização da shellcode. No entanto, a função vulnerável aceita um parâmetro (onde a shellcode será colocada).
Dessa forma, ao alterar o EIP por um endereço de **ret**, a próxima instrução será carregada (que é o endereço do primeiro argumento da função). Ou seja, a shellcode será carregada.
O exploit seria: SHELLCODE + Preenchimento (até o EIP) + **\&ret** (os próximos bytes da pilha apontam para o início da shellcode, pois o endereço do argumento passado é colocado na pilha)
Parece que funções como **strncpy**, uma vez completas, removem da pilha o endereço onde a shellcode estava armazenada, impossibilitando essa técnica. Ou seja, o endereço passado para a função como argumento (que armazena a shellcode) é modificado por um 0x00, então ao chamar o segundo **ret**, encontra-se com um 0x00 e o programa trava.
Observando como a pilha de um novo processo é construída no Linux, é possível desenvolver um exploit de modo que o programa seja iniciado em um ambiente onde a única variável seja a shellcode. O endereço dela pode ser calculado como: addr = 0xbfffffff - 4 - strlen(NOME\_do\_executável\_completo) - strlen(shellcode)
Como o ESP está sempre apontando para o início da pilha, essa técnica consiste em substituir o EIP pelo endereço de uma chamada para **jmp esp** ou **call esp**. Assim, a shellcode é armazenada após a sobrescrita do EIP, pois após executar o **ret**, o ESP estará apontando para o próximo endereço, exatamente onde a shellcode foi armazenada.
Caso o ASLR não esteja ativo no Windows ou Linux, é possível chamar **jmp esp** ou **call esp** armazenados em algum objeto compartilhado. Se o ASLR estiver ativo, pode-se procurar dentro do próprio programa vulnerável.
Além disso, o fato de poder colocar a shellcode após a corrupção do EIP em vez de no meio da pilha permite que as instruções push ou pop executadas no meio da função não afetem a shellcode (o que poderia ocorrer se estivesse no meio da pilha da função).
De forma muito semelhante a isso, se soubermos que uma função retorna o endereço onde a shellcode está armazenada, é possível chamar **call eax** ou **jmp eax (ret2eax).**
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:
Se passarmos um número negativo como primeiro parâmetro, será exibido que len <256epassaremosporessefiltro,etambémstrlen(buffer)serámenorquel,poisléumunsignedinteserámuitogrande.
Esse tipo de overflow não busca escrever algo no processo do programa, mas sim contornar filtros mal projetados para explorar outras vulnerabilidades.
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.
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**.
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.
Uma string de formatação também pode ser abusada para **extrair conteúdo** da memória do programa.\
Por exemplo, na seguinte situação há uma **variável local na pilha apontando para uma flag**. Se você **encontrar** onde na **memória** o **ponteiro** para a **flag** está, você pode fazer o **printf acessar** esse **endereço** e **imprimir** a **flag**:
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**!
Normalmente você encontrará a seção **DTOR****entre** os valores `ffffffff` e `00000000`. Portanto, se você apenas ver esses valores, significa que **não há nenhuma função registrada**. Portanto, **sobrescreva** o **`00000000`** com o **endereço** do **shellcode** para executá-lo.
O **sprintf** move uma string formatada para uma **variável**. Portanto, você poderia abusar da **formatação** de uma string para causar um **estouro de buffer na variável** para onde o conteúdo é copiado.\
Por exemplo, a carga `%.44xAAAA` irá **escrever 44B+"AAAA" na variável**, o que pode causar um estouro de buffer.
**`atexit()`** é uma função para a qual **outras funções são passadas como parâmetros**. Essas **funções** serão **executadas** ao executar um **`exit()`** ou o **retorno** do **main**.\
Se você puder **modificar** o **endereço** de qualquer uma dessas **funções** para apontar para um shellcode, por exemplo, você **obterá controle** sobre o **processo**, mas atualmente isso é mais complicado.\
Atualmente, os **endereços das funções** a serem executadas estão **ocultos** por várias estruturas e, finalmente, o endereço para o qual apontam não são os endereços das funções, mas são **criptografados com XOR** e deslocamentos com uma **chave aleatória**. Portanto, atualmente esse vetor de ataque não é muito útil, pelo menos em x86 e x64\_86.\
A função de **criptografia** é **`PTR_MANGLE`**. **Outras arquiteturas** como m68k, mips32, mips64, aarch64, arm, hppa... **não implementam a função de criptografia** porque **retornam o mesmo** que receberam como entrada. Portanto, essas arquiteturas seriam atacáveis por esse vetor.
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.
Cada objeto de uma **classe** tem um **VPtr** que é um **ponteiro** para o array de sua classe. O VPtr faz parte do cabeçalho de cada objeto, então se uma **sobrescrita** do **VPtr** for alcançada, poderia ser **modificado** para **apontar** para um método fictício para que a execução de uma função vá para o shellcode.
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).
Consiste em carregar as bibliotecas compartilhadas de 0x00000000 a 0x00ffffff para que sempre haja um byte 0x00. No entanto, isso realmente não impede quase nenhum ataque, especialmente em little endian.
Consiste em realizar um ROP de forma que a função strcpy@plt (da plt) seja chamada e aponte para a entrada da GOT e copie o primeiro byte da função que se deseja chamar (system()). Em seguida, o mesmo é feito apontando para GOT+1 e copiando o 2º byte de system()... Por fim, a chamada é feita para o endereço armazenado na GOT que será system()
Ao liberar um chunk, se algum dos chunks contíguos estiver livre, eles são fundidos pela macro unlink() e o novo chunk maior é passado para frontlink() para que seja inserido no bin apropriado.
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->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Isso resulta na escrita de 4 bytes a partir do 8º byte do shellcode, então a primeira instrução do shellcode deve ser um jmp para pular isso e chegar a uns nops que levem ao restante do shellcode.
Após o shellcode, insira preenchimento até chegar ao campo prev\_size e size do próximo chunk. Nestes locais, insira 0xfffffff0 (para sobrescrever o prev\_size para que tenha o bit que indica que está livre) e “-4” (0xfffffffc) no size (para que, ao verificar no 3º chunk se o 2º estava livre, na realidade vá para o prev\_size modificado que dirá que está livre) -> Assim, quando o free() investigar, ele irá para o size do 3º, mas na realidade irá para o 2º - 4 e pensará que o 2º chunk está livre. E então chamará **unlink()**.
Ao chamar unlink(), ele usará os primeiros dados do 2º chunk como P->fd, então o endereço que se deseja sobrescrever - 12 (pois em FD->bk ele adicionará 12 ao endereço armazenado em FD) será inserido lá. E nesse endereço, insira o segundo endereço encontrado no 2º chunk, que deve ser o endereço do shellcode (P->bk falso).
**fake\_size = pack("\<I”, 0xfffffffc) #-4, para que piense que el “size” del 3º trozo está 4bytes detrás (apunta a prev\_size) pues es ahí donde mira si el 2º trozo está libre**
**got\_free = pack("\<I", 0x08048300 - 12) #Dirección de free() en la plt-12 (será la dirección que se sobrescrita para que se lanza la shellcode la 2º vez que se llame a free)**
**payload += prev\_size + fake\_size + got\_free + addr\_sc #Se modifica el 2º trozo, el got\_free apunta a donde vamos a guardar la direccion addr\_sc + 12**
El chunck a lo usamos para sobreescribir el b de forma que el el size tenga el bit PREV\_INUSE desactivado de forma que piense que el chunck a está libre.
Entonces, el programa se pensará que “a” está libre y en un bin, por lo que llamará a unlink() para desenlazarlo. Sin embargo, como la cabecera PREV\_SIZE vale -4. Se pensará que el trozo de “a” realmente empieza en b+4. Es decir, hará un unlink() a un trozo que comienza en b+4, por lo que en b+12 estará el puntero “fd” y en b+16 estará el puntero “bk”.
Se llama a frontlink cuando se libera algo y ninguno de sus trozos contiguos no son libres, no se llama a unlink() sino que se llama directamente a frontlink().
De esta forma logrando sobres cribar en dos mallocs de forma descontrolada y en uno de forma controlada pero que solo se libera ese uno, podemos hacer un exploit.
En caso de querer volver a usar uno se asignaría sin problemas. En caso de querer usar otro, se le asignaría el mismo espacio por lo que tendríamos los punteros “fd” y “bk” falseados con los datos que escribirá la reserva anterior.
Solo una llamada a free() es necesaria para provocar la ejecución de código arbitrario. Interesa buscar un segundo trozo que puede ser desbordado por uno anterior y liberado.
En \[1] comprueba el campo size el bit NON\_MAIN\_ARENA, el cual se puede alterar para que la comprobación devuelva true y ejecute heap\_for\_ptr() que hace un and a “mem” dejando a 0 los 2.5 bytes menos importantes (en nuestro caso de 0x0804a000 deja 0x08000000) y accede a 0x08000000->ar\_ptr (como si fuese un struct heap\_info)
De esta forma si podemos controlar un trozo por ejemplo en 0x0804a000 y se va a liberar un trozo en **0x081002a0** podemos llegar a la dirección 0x08100000 y escribir lo que queramos, por ejemplo **0x0804a000**. Cuando este segundo trozo se libere se encontrará que heap\_for\_ptr(ptr)->ar\_ptr devuelve lo que hemos escrito en 0x08100000 (pues se aplica a 0x081002a0 el and que vimos antes y de ahí se saca el valor de los 4 primeros bytes, el ar\_ptr)
Por lo tanto si en av->bins\[2] escribimos el valor de \_\_DTOR\_END\_\_-12 en la última instrucción se escribirá en \_\_DTOR\_END\_\_ la dirección del segundo trozo.
En la dirección que caiga la dirección del segundo trozo con los últimos 5 ceros hay que escribir la dirección a este primer trozo para que heap\_for\_ptr() piense que el ar\_ptr está al inicio del primer trozo y saque de ahí el av->bins\[2]
De esta forma se llamará a \_int\_free(TROZO1, TROZO2) y seguirá las instrucciones para escribir en \_\_DTOR\_END\_\_ la dirección del prev\_size del TROZO2 el cual saltará a la shellcode.
Para aplicar esta técnica, é necessário atender a alguns requisitos adicionais que complicam um pouco mais o payload.
Esta técnica não é mais aplicável, pois foi aplicado quase o mesmo patch que para unlink. Verifica-se se o novo local para onde se aponta também está apontando para ele.
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.
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
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).
Devido aos nulos de \_DTOR\_END\_ e às poucas direções na GOT, nenhum desses endereços dessas seções serve para serem sobrescritos, então vejamos como aplicar fastbin para atacar a pilha.
Além disso, em **av->system\_mem** (1484 bytes acima da posição na pilha) haverá bastante lixo que nos permitirá ignorar a verificação que é realizada.
Além disso, é necessário que o chunk adjacente ao liberado seja maior que 8 -> Como mencionamos que o tamanho do chunk liberado é 16, neste chunk falso só precisamos colocar um tamanho maior que 8 (além disso, a shellcode estará no chunk liberado, então no início teremos que colocar um jmp que caia em nops que vêm após o campo size do novo chunk falso).
Neste caso, buscamos ter um ponteiro para um malloc que possa ser alterado pelo atacante (por exemplo, o ponteiro está na pilha abaixo de um possível overflow para uma variável).
Assim, poderíamos fazer com que esse ponteiro apontasse para onde quer que fosse. No entanto, nem todo local é válido, o tamanho do chunk falso deve ser menor que av->max\_fast e mais especificamente igual ao tamanho solicitado em uma chamada futura para malloc()+8. Portanto, se soubermos que após esse ponteiro vulnerável é feita uma chamada para malloc(40), o tamanho do chunk falso deve ser igual a 48.
Por exemplo, se o programa perguntar ao usuário por um número, poderíamos inserir 48 e apontar o ponteiro de malloc modificável para os próximos 4 bytes (que poderiam pertencer ao EBP com sorte, assim o 48 ficaria atrás, como se fosse o cabeçalho size). Além disso, o endereço ptr-4+48 deve atender a várias condições (sendo neste caso ptr=EBP), ou seja, 8 <ptr-4+48<av->system\_mem.
Caso isso seja cumprido, quando a próxima chamada para malloc que dissemos que era malloc(40) for feita, o endereço atribuído será o endereço do EBP. Caso o atacante também possa controlar o que é escrito nesse malloc, ele pode sobrescrever tanto o EBP quanto o EIP com o endereço desejado.
Acredito que isso ocorre porque quando o free() é chamado, ele armazenará que no endereço apontado pelo EBP da pilha há um chunk de tamanho perfeito para o novo malloc() que está sendo reservado, então ele atribui esse endereço.
* 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.
A vítima obtém o valor da direção do chunk wilderness atual (o av->top atual) e o restante é exatamente a soma dessa direção mais a quantidade de bytes solicitados por malloc(). Portanto, se \&EIP-8 estiver em 0xbffff224 e av->top contiver 0x080c2788, então a quantidade que precisamos reservar no malloc controlado para que av->top aponte para $EIP-8 para o próximo malloc() será:
0xbffff224 - 0x080c2788 = 3086207644.
Assim, o valor alterado será armazenado em av->top e o próximo malloc apontará para o EIP e poderá ser sobrescrito.
É importante que o tamanho do novo chunk wilderness seja maior que a solicitação feita pelo último malloc(). Ou seja, se o wilderness estiver apontando para \&EIP-8, o tamanho ficará exatamente no campo EBP da pilha.
Os chunks liberados são inseridos no bin de acordo com seu tamanho. Mas antes de serem inseridos, são armazenados em unsorted bins. Quando um chunk é liberado, ele não é imediatamente colocado em seu bin, mas permanece em unsorted bins. Em seguida, se um novo chunk for reservado e o anterior liberado puder ser útil, ele será retornado, mas se for reservado um chunk maior, o chunk liberado em unsorted bins será colocado em seu bin apropriado.
Reserve dois malloc, de modo que o primeiro possa sofrer overflow após o segundo ter sido liberado e inserido em seu bin (ou seja, um malloc maior que o segundo deve ser reservado antes do overflow).
O malloc reservado com o endereço escolhido pelo atacante deve ser controlado pelo atacante.
O objetivo é o seguinte: se pudermos fazer um overflow em um heap que tenha um pedaço liberado e em seu bin abaixo, podemos alterar seu ponteiro bk. Ao alterar o ponteiro bk e se esse pedaço se tornar o primeiro da lista do bin e for reservado, o bin será enganado e informado de que o próximo pedaço da lista está na falsa direção que escolhemos (como stack ou GOT, por exemplo). Portanto, se outro pedaço for reservado e o atacante tiver permissões nele, um pedaço na posição desejada será fornecido e poderá ser escrito.
Após liberar o pedaço modificado, é necessário reservar um pedaço maior do que o liberado, para que o pedaço modificado saia dos unsorted bins e seja inserido em seu bin.
Uma vez no bin, é hora de modificar o ponteiro bk através do overflow para que aponte para o endereço que desejamos sobrescrever.
Assim, o bin deve esperar até que malloc() seja chamado várias vezes para que o bin modificado seja usado novamente e engane o bin, fazendo-o acreditar que o próximo pedaço está na direção falsa. Em seguida, o pedaço desejado será fornecido.
Para que a vulnerabilidade seja explorada o mais rápido possível, o ideal seria: reservar o pedaço vulnerável, reservar o pedaço a ser modificado, liberar esse pedaço, reservar um pedaço maior do que o a ser modificado, modificar o pedaço (vulnerabilidade), reservar um pedaço do mesmo tamanho do vulnerado e reservar um segundo pedaço do mesmo tamanho, que apontará para o endereço escolhido.
Para proteger esse ataque, é usada a verificação típica de que o pedaço "não" é falso: verifica-se se bck->fd está apontando para a vítima. Ou seja, no nosso caso, se o ponteiro fd* do pedaço falso apontado na pilha está apontando para a vítima. Para contornar essa proteção, o atacante deve ser capaz de escrever de alguma forma (provavelmente na pilha) no endereço correto da vítima. Para que pareça um pedaço verdadeiro.
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(asubtraçãonãopodesernegativaporqueécomparadacomumvalornãoassinado).
Além disso, foi introduzido um patch para torná-lo ainda mais complicado.
**Heap Spraying**
Basicamente consiste em reservar toda a memória possível para heaps e preenchê-los com um colchão de nops seguido de uma shellcode. Além disso, o colchão é preenchido com 0x0c. Assim, tenta-se saltar para o endereço 0x0c0c0c0c e, se alguma direção for sobrescrita com esse colchão, o controle será transferido para lá. Basicamente, a tática é reservar o máximo possível para ver se algum ponteiro é sobrescrito e saltar para 0x0c0c0c0c, esperando que haja nops lá.
Consiste em semear a memória por meio de reservas e liberações para que pedaços reservados fiquem entre pedaços livres. O buffer a ser estourado será colocado em um desses pedaços.
<summary><strong>Aprenda hacking AWS do zero ao hero com</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* 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)!
* 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).