Off by one overflow
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ter apenas acesso a um overflow de 1B permite que um atacante modifique o campo size
do próximo chunk. Isso permite manipular quais chunks são realmente liberados, potencialmente gerando um chunk que contém outro chunk legítimo. A exploração é semelhante a double free ou chunks sobrepostos.
Existem 2 tipos de vulnerabilidades off by one:
Byte arbitrário: Este tipo permite sobrescrever esse byte com qualquer valor
Byte nulo (off-by-null): Este tipo permite sobrescrever esse byte apenas com 0x00
Um exemplo comum dessa vulnerabilidade pode ser visto no seguinte código onde o comportamento de strlen
e strcpy
é inconsistente, o que permite definir um byte 0x00 no início do próximo chunk.
Isso pode ser explorado com a House of Einherjar.
Se usando Tcache, isso pode ser aproveitado para uma situação de double free.
Entre outras verificações, agora sempre que um chunk é liberado, o tamanho anterior é comparado com o tamanho configurado nos metadados do chunk, tornando esse ataque bastante complexo a partir da versão 2.28.
Este ataque não está mais funcionando devido ao uso de Tcaches.
Além disso, se você tentar abusar dele usando chunks maiores (para que os tcaches não estejam envolvidos), você receberá o erro: malloc(): invalid next size (unsorted)
Fazer um chunk ser contido dentro de outro chunk, de modo que o acesso de escrita sobre esse segundo chunk permita sobrescrever o contido.
Off by one overflow para modificar as informações de metadados de tamanho.
Alocar três chunks A
, B
e C
(digamos tamanhos 0x20), e outro para evitar a consolidação com o top-chunk.
Liberar C
(inserido na lista de chunks livres do Tcache de 0x20).
Usar o chunk A
para transbordar sobre B
. Abusar do off-by-one para modificar o campo size
de B
de 0x21 para 0x41.
Agora temos B
contendo o chunk livre C
.
Liberar B
e alocar um chunk de 0x40 (ele será colocado aqui novamente).
Podemos modificar o ponteiro fd
de C
, que ainda está livre (envenenamento do Tcache).
3 chunks de memória (a, b, c) são reservados um após o outro. Em seguida, o do meio é liberado. O primeiro contém uma vulnerabilidade de off by one e o atacante a abusa com um 0x00 (se o byte anterior fosse 0x10, isso faria o chunk do meio indicar que é 0x10 menor do que realmente é).
Em seguida, mais 2 chunks menores são alocados no chunk liberado do meio (b), no entanto, como b + b->size
nunca atualiza o chunk c porque o endereço apontado é menor do que deveria.
Então, b1 e c são liberados. Como c - c->prev_size
ainda aponta para b (b1 agora), ambos são consolidados em um chunk. No entanto, b2 ainda está entre b1 e c.
Finalmente, um novo malloc é realizado reclamando esta área de memória que na verdade vai conter b2, permitindo que o proprietário do novo malloc controle o conteúdo de b2.
Esta imagem explica perfeitamente o ataque:
Off-by-one devido ao strlen
considerar o campo size
do próximo chunk.
Tcache está sendo usado, então um ataque geral off-by-one funciona para obter uma primitiva de escrita arbitrária com envenenamento do Tcache.
É possível abusar de um off by one para vazar um endereço do heap porque o byte 0x00 do final de uma string está sendo sobrescrito pelo próximo campo.
A escrita arbitrária é obtida abusando da escrita off by one para fazer o ponteiro apontar para outro lugar onde uma estrutura falsa com ponteiros falsos será construída. Em seguida, é possível seguir o ponteiro desta estrutura para obter escrita arbitrária.
O endereço da libc é vazado porque se o heap for estendido usando mmap, a memória alocada por mmap tem um deslocamento fixo em relação à libc.
Finalmente, a escrita arbitrária é abusada para escrever no endereço de __free_hook com um one gadget.
Há uma vulnerabilidade de off by one NULL na função getline
que lê linhas de entrada do usuário. Esta função é usada para ler a "chave" do conteúdo e não o conteúdo.
No writeup, 5 chunks iniciais são criados:
chunk1 (0x200)
chunk2 (0x50)
chunk5 (0x68)
chunk3 (0x1f8)
chunk4 (0xf0)
chunk defense (0x400) para evitar a consolidação com o top chunk.
Então, os chunks 1, 5 e 3 são liberados, então:
[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]
[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]