Off by one overflow

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Informazioni di base

Avere accesso a un 1B overflow permette a un attacker di modificare il campo size del chunk successivo. Questo consente di manomettere quali chunk vengono effettivamente freed, potenzialmente generando un chunk che contiene un altro chunk legittimo. L’exploitation è simile a double free o a chunk sovrapposti.

Esistono 2 tipi di vulnerabilità off by one:

  • Byte arbitrario: Questo tipo permette di sovrascrivere quel byte con qualsiasi valore
  • Null byte (off-by-null): Questo tipo permette di sovrascrivere quel byte solo con 0x00
  • Un esempio comune di questa vulnerabilità può essere visto nel seguente codice dove il comportamento di strlen e strcpy è inconsistente, il che permette di impostare un byte 0x00 all’inizio del chunk successivo.
  • Questo può essere sfruttato con la House of Einherjar.
  • Se si usa Tcache, questo può essere sfruttato per ottenere una situazione di double free.
Off-by-null ```c // From https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/off_by_one/ int main(void) { char buffer[40]=""; void *chunk1; chunk1 = malloc(24); puts("Get Input"); gets(buffer); if(strlen(buffer)==24) { strcpy(chunk1,buffer); } return 0; } ```

Tra gli altri controlli, ora ogni volta che un chunk è free il previous size viene confrontato con la size configurata nel chunk dei metadata, rendendo questo attacco piuttosto complesso a partire dalla versione 2.28.

Esempio di codice:

Obiettivo

  • Fare in modo che un chunk sia contenuto all’interno di un altro chunk in modo che scrivendo sul secondo chunk si possa sovrascrivere quello contenuto

Requisiti

  • Off by one overflow per modificare le informazioni dei metadata della size

General off-by-one attack

  • Allocate three chunks A, B and C (say sizes 0x20), and another one to prevent consolidation with the top-chunk.
  • Free C (inserted into 0x20 Tcache free-list).
  • Use chunk A to overflow on B. Abuse off-by-one to modify the size field of B from 0x21 to 0x41.
  • Now we have B containing the free chunk C
  • Free B and allocate a 0x40 chunk (it will be placed here again)
  • We can modify the fd pointer from C, which is still free (Tcache poisoning)

Off-by-null attack

  • 3 chunks of memory (a, b, c) are reserved one after the other. Then the middle one is freed. The first one contains an off by one overflow vulnerability and the attacker abuses it with a 0x00 (if the previous byte was 0x10 it would make he middle chunk indicate that it’s 0x10 smaller than it really is).
  • Then, 2 more smaller chunks are allocated in the middle freed chunk (b), however, as b + b->size never updates the c chunk because the pointed address is smaller than it should.
  • Then, b1 and c gets freed. As c - c->prev_size still points to b (b1 now), both are consolidated in one chunk. However, b2 is still inside in between b1 and c.
  • Finally, a new malloc is performed reclaiming this memory area which is actually going to contain b2, allowing the owner of the new malloc to control the content of b2.

This image explains perfectly the attack:

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

Note sul hardening & bypass di glibc moderno (>=2.32)

  • Safe-Linking ora protegge ogni puntatore della singly linked bin memorizzando fd = ptr ^ (chunk_addr >> 12), quindi un off-by-one che modifica solo il byte basso della size di solito richiede anche un heap leak per ricalcolare la maschera XOR prima che il Tcache poisoning funzioni.
  • Un pratico leakless trick è “double-protect” un puntatore: codifica un puntatore che già controlli con PROTECT_PTR, poi riutilizza lo stesso gadget per codificare il puntatore falsificato in modo che il controllo di allineamento passi senza rivelare nuovi indirizzi.
  • Workflow for Safe-Linking + single-byte corruptions:
  1. Grow the victim chunk until it fully covers a freed chunk you already control (overlapping-chunk setup).
  2. Leak any heap pointer (stdout, UAF, partially controlled struct) and derive the key heap_base >> 12.
  3. Re-encode free-list pointers before writing them—stage the encoded value inside user data and memcpy it later if you only own single-byte writes.
  4. Combine with Tcache bin attacks to redirect allocations into __free_hook or tcache_perthread_struct entries once the forged pointer is properly encoded.

Un helper minimale per esercitarsi nel passaggio di encode/decode durante il debug di exploit moderni:

def protect(ptr, chunk_addr):
return ptr ^ (chunk_addr >> 12)

def reveal(encoded, chunk_addr):
return encoded ^ (chunk_addr >> 12)

chunk = 0x55555555c2c0
encoded_fd = protect(0xdeadbeefcaf0, chunk)
print(hex(reveal(encoded_fd, chunk)))  # 0xdeadbeefcaf0

Obiettivo reale recente: glibc __vsyslog_internal off-by-one (CVE-2023-6779)

  • Nel gennaio 2024 Qualys ha descritto CVE-2023-6779, un off-by-one in __vsyslog_internal() che si attiva quando le stringhe di formato di syslog()/vsyslog() superano INT_MAX, quindi il terminatore \0 corrompe il byte meno significativo del campo size del chunk successivo su sistemi glibc 2.37–2.39 (Qualys advisory).
  • La loro pipeline di exploit su Fedora 38:
  1. Costruire un ident troppo lungo per openlog() in modo che vasprintf ritorni un buffer heap accanto a dati controllati dall’attaccante.
  2. Chiamare syslog() per corrompere il byte size | prev_inuse del chunk vicino, fare free su quel chunk e forzare la consolidazione che sovrappone i dati controllati dall’attaccante.
  3. Usare la vista sovrapposta per corrompere i metadata di tcache_perthread_struct e indirizzare la successiva allocazione verso __free_hook, sovrascrivendolo con system/un one_gadget per root.
  • Per riprodurre la scrittura corruttrice in un harness, eseguire un fork con un argv[0] gigantesco, chiamare openlog(NULL, LOG_PID, LOG_USER) e poi syslog(LOG_INFO, "%s", payload) dove payload = b"A" * 0x7fffffff; i heap bins di pwndbg mostrano immediatamente la sovrascrittura di un singolo byte.
  • Ubuntu traccia il bug come CVE-2023-6779, documentando la stessa troncatura a INT che rende questo un primitivo off-by-one affidabile.

Altri esempi e riferimenti

  • https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
  • Bon-nie-appetit. HTB Cyber Apocalypse CTF 2022
  • Off-by-one dovuto a strlen che considera il campo size del chunk successivo.
  • Viene usato Tcache, quindi un attacco off-by-one generale funziona per ottenere un primitivo di arbitrary write tramite Tcache poisoning.
  • Asis CTF 2016 b00ks
  • È possibile abusare di un off-by-one per ottenere un leak di un indirizzo dall’heap perché il byte 0x00 alla fine di una stringa viene sovrascritto dal campo successivo.
  • L’arbitrary write si ottiene sfruttando la scrittura off-by-one per far puntare il puntatore in un’altra posizione dove verrà costruita una fake struct con puntatori falsi. Poi è possibile seguire il puntatore di questa struct per ottenere arbitrary write.
  • L’indirizzo di libc viene leakato perché se l’heap è esteso tramite mmap, la memoria allocata da mmap ha un offset fisso rispetto a libc.
  • Infine l’arbitrary write viene sfruttato per scrivere nell’indirizzo di __free_hook con un one gadget.
  • plaidctf 2015 plaiddb
  • C’è una vulnerabilità NULL off-by-one nella funzione getline che legge le linee di input utente. Questa funzione viene usata per leggere la “key” del contenuto e non il contenuto.
  • Nel writeup vengono creati 5 chunk iniziali:
  • chunk1 (0x200)
  • chunk2 (0x50)
  • chunk5 (0x68)
  • chunk3 (0x1f8)
  • chunk4 (0xf0)
  • chunk defense (0x400) per evitare la consolidazione con il top chunk
  • Poi i chunk 1, 5 e 3 vengono liberati, quindi:

[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]

- Poi, abusando di chunk3 (0x1f8) il null off-by-one viene sfruttato per scrivere il prev_size a `0x4e0`.
- Nota come le dimensioni dei chunk inizialmente allocati 1, 2, 5 e 3 più gli header di 4 di questi chunk equivalgono a `0x4e0`: `hex(0x1f8 + 0x10 + 0x68 + 0x10 + 0x50 + 0x10 + 0x200) = 0x4e0`
- Poi il chunk 4 viene liberato, generando un chunk che consuma tutti i chunk fino all'inizio:
- ```python
[ 0x4e0 Chunk 1-2-5-3 (free) ] [ 0xf0 Chunk 4 (corrupted) ] [ 0x400 Chunk defense ]

[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]

- Poi vengono allocati `0x200` bytes riempiendo il chunk originale 1
- E altri 0x200 byte vengono allocati, distruggendo chunk2; quindi non c'è alcun fucking leak e questo non funziona — forse non si doveva fare
- Poi alloca un altro chunk con 0x58 "a" (sovrascrivendo chunk2 e raggiungendo chunk5) e modifica il `fd` del fast bin chunk di chunk5 puntandolo a `__malloc_hook`
- Poi viene allocato un chunk di 0x68 in modo che il fake fast bin chunk in `__malloc_hook` sia il fast bin chunk successivo
- Infine viene allocato un nuovo fast bin chunk di 0x68 e `__malloc_hook` viene sovrascritto con l'indirizzo di un `one_gadget`

## Riferimenti

- [Qualys Security Advisory – CVE-2023-6246/6779/6780](https://www.qualys.com/2024/01/30/cve-2023-6246/syslog.txt)
- [Ubuntu Security – CVE-2023-6779](https://ubuntu.com/security/CVE-2023-6779)
- [Breaking Safe-Linking in Modern Glibc – Google CTF 2022 "saas" analysis](https://blog.csdn.net/2402_86373248/article/details/148717274)

> [!TIP]
> Impara e pratica il hacking AWS:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Impara e pratica il hacking GCP: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Impara e pratica il hacking Azure: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Supporta HackTricks</summary>
>
> - Controlla i [**piani di abbonamento**](https://github.com/sponsors/carlospolop)!
> - **Unisciti al** 💬 [**gruppo Discord**](https://discord.gg/hRep4RUj7f) o al [**gruppo telegram**](https://t.me/peass) o **seguici** su **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Condividi trucchi di hacking inviando PR ai** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repos github.
>
> </details>