Off by one overflow

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

基本情報

1B のオーバーフローにアクセスできるだけで、攻撃者は次のチャンクの size フィールドを変更できます。これにより、実際に free されるチャンクを改ざんでき、別の正当なチャンクを含むチャンクを生成する可能性があります。エクスプロイト手法は double free や overlapping chunks と似ています。

オフバイワン脆弱性には 2 種類があります:

  • Arbitrary byte: この種類は当該バイトを任意の値で上書きできます
  • Null byte (off-by-null): この種類は当該バイトを 0x00 のみで上書きできます
  • この脆弱性の一般的な例は、strlenstrcpy の振る舞いが不整合な次のようなコードに見られ、次のチャンク先頭に 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; } ```

他のチェックの一環として、チャンクが free になるたびに previous size がメタデータのチャンクに設定された size と比較されるようになり、この攻撃はバージョン 2.28 以降かなり複雑になっています。

Code example:

Goal

  • あるチャンクが別のチャンクの内部に収まるようにして、その外側のチャンクへの書き込みで内側のチャンクを上書きできるようにすること

Requirements

  • size メタデータ情報を変更できる off by one overflow

General off-by-one attack

  • 3 つのチャンク A, B, C(例えばサイズ 0x20)と、top-chunk との統合を防ぐためのもう一つを確保する。
  • C を free する(0x20 の Tcache free-list に挿入される)。
  • チャンク A を使って B に overflow する。off-by-one を悪用して Bsize フィールドを 0x21 から 0x41 に変更する。
  • これで B が free チャンク C を内包している状態になる。
  • B を free して 0x40 のチャンクを割り当てる(ここに配置される)。
  • まだ free のままの C から fd ポインタを変更できる(Tcache poisoning)。

Off-by-null attack

  • 3 つのメモリチャンク(a, b, c)が連続して確保される。中央のチャンクが free される。最初のチャンクには off by one の overflow 脆弱性があり、攻撃者は 0x00 を使ってこれを悪用する(もし前のバイトが 0x10 であれば、中央チャンクが実際より 0x10 小さいと示すようになる)。
  • その後、中央の free チャンク(b)にさらに 2 つの小さなチャンクが割り当てられる。しかし、b + b->size は指すアドレスが本来より小さいため c チャンクを更新しない。
  • 次に、b1 と c が free される。c - c->prev_size がまだ b(現在は b1)を指しているため、両者は一つのチャンクに統合される。しかし b2 はその間にまだ残っている。
  • 最後に新たな malloc を行うと、このメモリ領域が回収され、実際には b2 を含むことになるため、新しい malloc の所有者は b2 の内容を制御できる。

この画像は攻撃を完璧に説明しています:

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

Modern glibc hardening & bypass notes (>=2.32)

  • Safe-Linking は現在すべての singly linked bin ポインタを fd = ptr ^ (chunk_addr >> 12) として保護しているため、size の下位バイトだけを反転させるような off-by-one は、通常 Tcache poisoning が機能する前に XOR マスクを再計算するための heap leak を必要とします。
  • 実用的な leakless トリックとしては、ポインタを「二重に保護」する方法があります。すでに制御しているポインタを PROTECT_PTR でエンコードし、同じガジェットを再利用して偽造ポインタをエンコードすれば、アラインメントチェックが新しいアドレスを明かさずに通過します。
  • Safe-Linking と単一バイト破壊のワークフロー:
    1. 被害者チャンクを伸ばして、すでに制御している free チャンクを完全に覆う(overlapping-chunk のセットアップ)。
    2. 何らかの heap ポインタ(stdout、UAF、部分的に制御される構造体など)をリークし、キー heap_base >> 12 を導出する。
    3. 書き込む前に free-list ポインタを再エンコードする — 単一バイト書き込みしかできない場合は、エンコード済みの値をユーザデータ内にステージしてから後で memcpy する。
    4. 正しくエンコードされた偽造ポインタと組み合わせて、Tcache bin attacks を用い、割り当てを __free_hooktcache_perthread_struct のエントリへリダイレクトする。

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

Recent real-world target: glibc __vsyslog_internal off-by-one (CVE-2023-6779)

  • 2024年1月、QualysはCVE-2023-6779を詳細に報告しました。これは__vsyslog_internal()内のオフバイワンで、syslog()/vsyslog()のフォーマット文字列がINT_MAXを超えると発生し、終端の\0が次のチャンクの最下位のsizeバイトを破損させるもので、glibc 2.37–2.39のシステムで影響します(Qualys advisory)。
  • Their Fedora 38 exploit pipeline:
  1. 長すぎる openlog() の ident を作成して、vasprintf が attacker-controlled data の隣に heap buffer を返すようにする。
  2. syslog() を呼び出して隣接チャンクの size | prev_inuse バイトを破壊し、それを free して、attacker data と重なるように統合を強制する。
  3. オーバーラップしたビューを使って tcache_perthread_struct のメタデータを破壊し、次の割り当てを __free_hook に向けて、system/one_gadget(root用)で上書きする。
  • To reproduce the corrupting write in a harness, fork with a gigantic argv[0], call openlog(NULL, LOG_PID, LOG_USER) and then syslog(LOG_INFO, "%s", payload) where payload = b"A" * 0x7fffffff; pwndbg’s heap bins immediately shows the single-byte overwrite.
  • Ubuntu tracks the bug as CVE-2023-6779, documenting the same INT truncation that makes this a reliable off-by-one primitive.

Other Examples & References

  • https://heap-exploitation.dhavalkapil.com/attacks/shrinking_free_chunks
  • Bon-nie-appetit. HTB Cyber Apocalypse CTF 2022
  • Off-by-one because of strlen considering the next chunk’s size field.
  • Tcache is being used, so a general off-by-one attacks works to get an arbitrary write primitive with Tcache poisoning.
  • Asis CTF 2016 b00ks
  • It’s possible to abuse an off by one to leak an address from the heap because the byte 0x00 of the end of a string being overwritten by the next field.
  • Arbitrary write is obtained by abusing the off by one write to make the pointer point to another place were a fake struct with fake pointers will be built. Then, it’s possible to follow the pointer of this struct to obtain arbitrary write.
  • The libc address is leaked because if the heap is extended using mmap, the memory allocated by mmap has a fixed offset from libc.
  • Finally the arbitrary write is abused to write into the address of __free_hook with a one gadget.
  • plaidctf 2015 plaiddb
  • There is a NULL off by one vulnerability in the getline function that reads user input lines. This function is used to read the “key” of the content and not the content.
  • In the writeup 5 initial chunks are created:
  • chunk1 (0x200)
  • chunk2 (0x50)
  • chunk5 (0x68)
  • chunk3 (0x1f8)
  • chunk4 (0xf0)
  • chunk defense (0x400) to avoid consolidating with top chunk
  • Then chunk 1, 5 and 3 are freed, so:

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

- Then abusing chunk3 (0x1f8) the null off-by-one is abused writing the prev_size to `0x4e0`.
- Note how the sizes of the initially allocated chunks1, 2, 5 and 3 plus the headers of 4 of those chunks equals to `0x4e0`: `hex(0x1f8 + 0x10 + 0x68 + 0x10 + 0x50 + 0x10 + 0x200) = 0x4e0`
- Then, chunk 4 is freed, generating a chunk that consumes all the chunks till the beginning:
- ```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 ]

- Then, `0x200` bytes are allocated filling the original chunk 1
- And another 0x200 bytes are allocated and chunk2 is destroyed and therefore there isn't no fucking leak and this doesn't work? Maybe this shouldn't be done
- Then, it allocates another chunk with 0x58 "a"s (overwriting chunk2 and reaching chunk5) and modifies the `fd` of the fast bin chunk of chunk5 pointing it to `__malloc_hook`
- Then, a chunk of 0x68 is allocated so the fake fast bin chunk in `__malloc_hook` is the following fast bin chunk
- Finally, a new fast bin chunk of 0x68 is allocated and `__malloc_hook` is overwritten with a `one_gadget` address

## 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ハッキングを学び、実践する:<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ハッキングを学び、実践する:<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ハッキングを学び、実践する:<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)または[**テレグラムグループ**](https://t.me/peass)に参加するか、**Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**をフォローしてください。**
> - **[**HackTricks**](https://github.com/carlospolop/hacktricks)および[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud)のGitHubリポジトリにPRを提出してハッキングトリックを共有してください。**
>
> </details>