Off by one overflow

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Grundlegende Informationen

Having just access to a 1B overflow allows an attacker to modify the size field from the next chunk. This allows to tamper which chunks are actually freed, potentially generating a chunk that contains another legit chunk. The exploitation is similar to double free or overlapping chunks.

Es gibt 2 Arten von off-by-one-Schwachstellen:

  • Arbitrary byte: Diese Art ermöglicht, dieses Byte mit einem beliebigen Wert zu überschreiben
  • Null byte (off-by-null): Diese Art erlaubt nur, dieses Byte mit 0x00 zu überschreiben
  • Ein häufiges Beispiel für diese Schwachstelle ist im folgenden Code zu sehen, wo das Verhalten von strlen und strcpy inkonsistent ist, wodurch sich ein 0x00-Byte am Anfang des nächsten Chunks setzen lässt.
  • Dies kann mit der House of Einherjar ausgenutzt werden.
  • Wenn Tcache verwendet wird, kann dies zu einer double free-Situation führen.
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; } ```

Among other checks, now whenever a chunk is free the previous size is compared with the size configured in the metadata’s chunk, making this attack fairly complex from version 2.28.

Code-Beispiel:

Ziel

  • Einen chunk innerhalb eines anderen platzieren, sodass Schreibzugriff auf den zweiten chunk den enthaltenen chunk überschreiben kann

Voraussetzungen

  • Off by one overflow, um die size-Metadaten zu verändern

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. Missbrauche off-by-one, um das size-Feld von B von 0x21 auf 0x41 zu ändern.
  • 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

Moderne glibc-Härtung & Bypass-Hinweise (>=2.32)

  • Safe-Linking now protects every singly linked bin pointer by storing fd = ptr ^ (chunk_addr >> 12), so an off-by-one that only flips the low byte of size usually also needs a heap leak to recompute the XOR mask before Tcache poisoning works.
  • A practical leakless trick is to “double-protect” a pointer: encode a pointer you already control with PROTECT_PTR, then reuse the same gadget to encode your forged pointer so the alignment check passes without revealing new addresses.
  • 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.

Ein minimales Hilfsmittel, um den encode/decode-Schritt beim Debuggen moderner Exploits zu üben:

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

Jüngstes reales Ziel: glibc __vsyslog_internal off-by-one (CVE-2023-6779)

  • Im Januar 2024 beschrieb Qualys CVE-2023-6779, ein off-by-one in __vsyslog_internal(), das auftritt, wenn syslog()/vsyslog() Formatstrings INT_MAX überschreiten, sodass das terminierende \0 das niederwertigste size-Byte des nächsten Chunks auf glibc 2.37–2.39 Systemen beschädigt (Qualys advisory).
  • Ihre Fedora 38 Exploit-Pipeline:
  1. Erzeuge ein überlanges openlog() ident, sodass vasprintf einen Heap-Puffer neben angreiferkontrollierten Daten zurückgibt.
  2. Rufe syslog() auf, um das size | prev_inuse Byte des Nachbarchunks zu überschreiben, free() es und erzwinge Konsolidierung, die angreiferkontrollierte Daten überlappt.
  3. Nutze die überlappende Ansicht, um tcache_perthread_struct-Metadaten zu korruptieren und die nächste Allokation auf __free_hook zu richten, um es mit system/einem one_gadget für Root zu überschreiben.
  • Um den korrumpierenden Schreibzugriff in einem Harness zu reproduzieren, fork() mit einem riesigen argv[0], rufe openlog(NULL, LOG_PID, LOG_USER) auf und danach syslog(LOG_INFO, "%s", payload) wobei payload = b"A" * 0x7fffffff; pwndbg’s heap bins zeigt sofort das Ein-Byte-Overwrite.
  • Ubuntu führt den Bug als CVE-2023-6779, dokumentiert dieselbe INT-Trunkierung, die dies zu einer zuverlässigen off-by-one-Primitive macht.

Weitere Beispiele & Referenzen

  • https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
  • Bon-nie-appetit. HTB Cyber Apocalypse CTF 2022
  • Off-by-one verursacht durch strlen, das das size-Feld des nächsten Chunks berücksichtigt.
  • Tcache wird verwendet, daher funktioniert ein allgemeiner off-by-one-Angriff, um mit Tcache-Poisoning ein arbitrary write-Primitive zu erhalten.
  • Asis CTF 2016 b00ks
  • Es ist möglich, ein off-by-one auszunutzen, um eine Adresse vom Heap zu leak, weil das Byte 0x00 am Ende eines Strings vom nächsten Feld überschrieben wird.
  • Arbitrary write wird erreicht, indem der off-by-one-Schreibvorgang missbraucht wird, sodass ein Pointer auf einen anderen Ort zeigt, an dem eine gefälschte Struktur mit gefälschten Pointern aufgebaut wird. Dann kann man dem Pointer dieser Struktur folgen, um arbitrary write zu erhalten.
  • Die libc-Adresse wird geleakt, weil wenn der Heap mittels mmap erweitert wird, der durch mmap alloziierte Speicher einen festen Offset zu libc hat.
  • Schließlich wird der arbitrary write missbraucht, um an die Adresse von __free_hook mit einem one_gadget zu schreiben.
  • plaidctf 2015 plaiddb
  • Es gibt eine NULL off-by-one Verwundbarkeit in der Funktion getline, die Eingabezeilen vom Benutzer liest. Diese Funktion wird benutzt, um den “key” des Inhalts zu lesen und nicht den Inhalt selbst.
  • Im Writeup werden 5 initiale Chunks erstellt:
  • chunk1 (0x200)
  • chunk2 (0x50)
  • chunk5 (0x68)
  • chunk3 (0x1f8)
  • chunk4 (0xf0)
  • chunk defense (0x400) um eine Konsolidierung mit dem top chunk zu vermeiden
  • Dann werden chunk1, 5 und 3 freigegeben, also:

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

- Dann wird chunk3 (0x1f8) missbraucht: das NULL off-by-one wird verwendet, um `prev_size` auf `0x4e0` zu schreiben.
- Beachte, wie die Größen der initial allozierten chunks1, 2, 5 und 3 plus die Header von 4 dieser Chunks gleich `0x4e0` sind: `hex(0x1f8 + 0x10 + 0x68 + 0x10 + 0x50 + 0x10 + 0x200) = 0x4e0`
- Dann wird chunk 4 freigegeben und erzeugt einen Chunk, der alle Chunks bis zum Anfang aufnimmt:
- ```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 ]

- Dann werden `0x200` Bytes alloziert, die den ursprünglichen chunk 1 füllen
- Und noch einmal 0x200 Bytes werden alloziert und chunk2 wird zerstört und daher gibt es keinen fucking leak und das funktioniert nicht? Vielleicht sollte das nicht gemacht werden
- Danach wird ein weiterer Chunk mit 0x58 "a"s alloziert (überschreibt chunk2 und erreicht chunk5) und das `fd` des fastbin-Chunks von chunk5 wird so verändert, dass es auf `__malloc_hook` zeigt
- Dann wird ein Chunk von 0x68 alloziert, sodass der gefälschte fastbin-Chunk in `__malloc_hook` der folgende fastbin-Chunk ist
- Schließlich wird ein neuer fastbin-Chunk von 0x68 alloziert und `__malloc_hook` mit einer one_gadget-Adresse überschrieben

## Referenzen

- [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]
> Lernen & üben Sie AWS Hacking:<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;">\
> Lernen & üben Sie GCP Hacking: <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;">
> Lernen & üben Sie Azure Hacking: <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>Unterstützen Sie HackTricks</summary>
>
> - Überprüfen Sie die [**Abonnementpläne**](https://github.com/sponsors/carlospolop)!
> - **Treten Sie der** 💬 [**Discord-Gruppe**](https://discord.gg/hRep4RUj7f) oder der [**Telegram-Gruppe**](https://t.me/peass) bei oder **folgen** Sie uns auf **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Teilen Sie Hacking-Tricks, indem Sie PRs an die** [**HackTricks**](https://github.com/carlospolop/hacktricks) und [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub-Repos senden.
>
> </details>