Off by one overflow

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Основна інформація

Маючи доступ лише до 1B overflow, атакувальник може змінити поле size наступного чанка. Це дозволяє підміняти, які чанки фактично звільняються, потенційно створюючи чанк, який містить інший легітимний чанк. Експлуатація схожа на double free або overlapping chunks.

Існує 2 типи вразливостей off by one:

  • Arbitrary byte: Цей тип дозволяє перезаписати цей байт будь-яким значенням
  • Null byte (off-by-null): Цей тип дозволяє перезаписати цей байт лише значенням 0x00
  • Поширений приклад цієї вразливості можна побачити в наведеному нижче коді, де поведінка strlen і strcpy є неконсистентною, що дозволяє встановити байт 0x00 на початку наступного чанка.
  • Це можна експлуатувати за допомогою House of Einherjar.
  • Якщо використовується Tcache, це може призвести до ситуації 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; } ```

Серед інших перевірок, тепер коли chunk вільний, попередній size порівнюється з size, сконфігурованим у metadata’s chunk, що робить цю атаку доволі складною з версії 2.28.

Code example:

Goal

  • Зробити так, щоб один chunk опинявся всередині іншого, щоб запис у другий chunk дозволяв перезаписати вкладений.

Requirements

  • Off by one overflow для модифікації інформації 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

Modern glibc hardening & bypass notes (>=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.

A minimal helper to rehearse the encode/decode step while debugging modern exploits:

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

Недавня реальна ціль: glibc __vsyslog_internal off-by-one (CVE-2023-6779)

  • У січні 2024 Qualys детально описав CVE-2023-6779 — off-by-one всередині __vsyslog_internal(), який тригериться, коли форматні рядки syslog()/vsyslog() перевищують INT_MAX, через що термінуючий \0 пошкоджує найменш значущий байт поля size наступного chunk’а на системах glibc 2.37–2.39 (Qualys advisory).
  • Їхній Fedora 38 exploit pipeline:
  1. Згенерувати наддовгий ident для openlog(), щоб vasprintf повернув heap buffer поруч із даними, контрольованими атакуючим.
  2. Викликати syslog() щоб пошкодити байт size | prev_inuse сусіднього чанка, звільнити його і примусити консолідацію, яка перехоплює attacker data.
  3. Використати перекритий вигляд, щоб пошкодити метадані tcache_perthread_struct і направити наступне виділення в __free_hook, перезаписавши його system/one_gadget для отримання root.
  • Щоб відтворити пошкоджувальний запис у harness, форкнутися з гігантським argv[0], викликати openlog(NULL, LOG_PID, LOG_USER) і потім syslog(LOG_INFO, "%s", payload), де payload = b"A" * 0x7fffffff; pwndbg’s heap bins миттєво показує одно-байтове перезаписування.
  • Ubuntu відслідковує баг як CVE-2023-6779, документуючи те саме INT truncation, що робить це надійним примітивом off-by-one.

Інші приклади та посилання

  • https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
  • Bon-nie-appetit. HTB Cyber Apocalypse CTF 2022
  • Off-by-one через те, що strlen враховує поле size наступного chunk’а.
  • Використовується Tcache, тож загальна атака типу off-by-one працює для отримання примітива arbitrary write через Tcache poisoning.
  • Asis CTF 2016 b00ks
  • Можна зловживати off by one, щоб leak адресу з heap, оскільки байт 0x00 в кінці рядка перезаписується наступним полем.
  • Arbitrary write отримується шляхом зловживання off by one записом, щоб змусити покажчик вказувати в інше місце, де буде побудовано фейкову структуру з фейковими покажчиками. Потім можна пройти за покажчиком цієї структури, щоб отримати arbitrary write.
  • Адреса libc leak’иться, бо якщо heap розширюється через mmap, пам’ять, виділена mmap, має фіксований зсув відносно libc.
  • Нарешті arbitrary write зловживають, щоб записати в адресу __free_hook one_gadget’ом.
  • plaidctf 2015 plaiddb
  • Існує NULL off by one у функції getline, яка читає рядки вводу користувача. Ця функція використовується для читання “key” контенту, а не самого контенту.
  • У writeup створюються 5 початкових чанків:
  • chunk1 (0x200)
  • chunk2 (0x50)
  • chunk5 (0x68)
  • chunk3 (0x1f8)
  • chunk4 (0xf0)
  • chunk defense (0x400) щоб уникнути консолідації з top chunk
  • Потім chunk1, chunk5 і chunk3 звільняються, тож:

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

- Потім, зловживаючи chunk3 (0x1f8), NULL off-by-one використано для запису prev_size в `0x4e0`.
- Зверніть увагу, що сума розмірів початково алокованих chunk1, 2, 5 і 3 плюс заголовки 4 з цих чанків дорівнює `0x4e0`: `hex(0x1f8 + 0x10 + 0x68 + 0x10 + 0x50 + 0x10 + 0x200) = 0x4e0`
- Потім chunk4 звільняється, породжуючи chunk, який поглинає всі попередні до початку:
- ```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 ]

- Далі алокується 0x200 байт, заповнюючи початковий chunk1
- І ще одні 0x200 байт алокуються і chunk2 знищується, і тому немає ніякого fucking leak і це не працює? Можливо цього не слід робити
- Потім алокують ще один chunk з 0x58 "a" (перезаписуючи chunk2 і доходячи до chunk5) і змінюють `fd` fast bin чанка chunk5, вказуючи його в `__malloc_hook`
- Далі алокується chunk розміром 0x68, тож фейковий fast bin chunk у `__malloc_hook` стає наступним fast bin чанком
- Нарешті алокується новий fast bin chunk 0x68 і `__malloc_hook` перезаписується адресою `one_gadget`

## References

- [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]
> Вивчайте та практикуйте 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;">\
> Вивчайте та практикуйте 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;">
> Вивчайте та практикуйте 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>Підтримайте HackTricks</summary>
>
> - Перевірте [**плани підписки**](https://github.com/sponsors/carlospolop)!
> - **Приєднуйтесь до** 💬 [**групи Discord**](https://discord.gg/hRep4RUj7f) або [**групи telegram**](https://t.me/peass) або **слідкуйте** за нами в **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Діліться хакерськими трюками, надсилаючи PR до** [**HackTricks**](https://github.com/carlospolop/hacktricks) та [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) репозиторіїв на github.
>
> </details>