From 8a25bba5e0dd6ee88fbf6d171ec038826bbe9b78 Mon Sep 17 00:00:00 2001 From: Translator Date: Wed, 30 Jul 2025 12:40:41 +0000 Subject: [PATCH] Translated ['src/binary-exploitation/libc-heap/use-after-free/first-fit. --- .../libc-heap/use-after-free/first-fit.md | 97 ++++++++++++++++--- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/src/binary-exploitation/libc-heap/use-after-free/first-fit.md b/src/binary-exploitation/libc-heap/use-after-free/first-fit.md index 55d7af1ad..ce45e620b 100644 --- a/src/binary-exploitation/libc-heap/use-after-free/first-fit.md +++ b/src/binary-exploitation/libc-heap/use-after-free/first-fit.md @@ -4,18 +4,18 @@ ## **First Fit** -glibcを使用してプログラム内のメモリを解放すると、異なる「ビン」がメモリチャンクを管理するために使用されます。ここでは、一般的な2つのシナリオ:未ソートビンとファストビンについての簡略化された説明を示します。 +プログラムでglibcを使用してメモリを解放すると、異なる「ビン」がメモリチャンクを管理するために使用されます。ここでは、一般的な2つのシナリオ:未ソートビンとファストビンの簡略化された説明を示します。 ### Unsorted Bins -ファストチャンクでないメモリチャンクを解放すると、それは未ソートビンに入ります。このビンは、新しく解放されたチャンクが前方(「ヘッド」)に追加されるリストのように機能します。新しいメモリチャンクを要求すると、アロケータは未ソートビンの後方(「テイル」)を見て、十分な大きさのチャンクを探します。未ソートビンのチャンクが必要なサイズより大きい場合、それは分割され、前の部分が返され、残りの部分はビンに留まります。 +ファストチャンクでないメモリチャンクを解放すると、それは未ソートビンに入ります。このビンは、新しく解放されたチャンクが前面(「ヘッド」)に追加されるリストのように機能します。新しいメモリチャンクを要求すると、アロケータは未ソートビンの後ろ(「テイル」)から見て、十分な大きさのチャンクを探します。未ソートビンのチャンクが必要なサイズより大きい場合、それは分割され、前の部分が返され、残りの部分はビンに留まります。 例: - 300バイト(`a`)を割り当て、その後250バイト(`b`)を割り当て、`a`を解放して再度250バイト(`c`)を要求します。 - `a`を解放すると、それは未ソートビンに入ります。 -- その後再度250バイトを要求すると、アロケータはテイルにある`a`を見つけて分割し、リクエストに合う部分を返し、残りをビンに保持します。 -- `c`は以前の`a`を指し、`a`の内容で満たされます。 +- その後再度250バイトを要求すると、アロケータはテイルにある`a`を見つけて分割し、リクエストに合う部分を返し、残りはビンに保持します。 +- `c`は以前の`a`を指し、`a`の内容で埋められます。 ```c char *a = malloc(300); char *b = malloc(250); @@ -26,11 +26,7 @@ char *c = malloc(250); Fastbinsは小さなメモリチャンクに使用されます。未ソートのビンとは異なり、fastbinsは新しいチャンクを先頭に追加し、後入れ先出し(LIFO)の動作を作り出します。小さなメモリチャンクを要求すると、アロケータはfastbinの先頭から取得します。 -例: - -- 20バイトのチャンクを4つ(`a`、`b`、`c`、`d`)割り当てます。 -- それらを任意の順序で解放すると、解放されたチャンクはfastbinの先頭に追加されます。 -- その後、20バイトのチャンクを要求すると、アロケータはfastbinの先頭から最も最近解放されたチャンクを返します。 +Example: ```c char *a = malloc(20); char *b = malloc(20); @@ -45,18 +41,89 @@ b = malloc(20); // c c = malloc(20); // b d = malloc(20); // a ``` +--- +### 🔥 現代のglibcに関する考慮事項 (tcache ≥ 2.26) + +glibc 2.26以降、各スレッドは自分自身の**tcache**を保持し、これは未ソートビンの*前に*照会されます。したがって、ファーストフィットのシナリオは**次の場合にのみ**到達されます: + +1. リクエストされたサイズが**`tcache_max`**(デフォルトで64ビットでは0x420)より**大きい**、*または* +2. 対応するtcacheビンが**すでに満杯であるか、手動で空にされている**(7つの要素を割り当てて使用中に保持することによって)。 + +実際のエクスプロイトでは、通常、次のようなヘルパールーチンを追加します: +```c +// Drain the tcache for a given size +for(int i = 0; i < 7; i++) pool[i] = malloc(0x100); +for(int i = 0; i < 7; i++) free(pool[i]); +``` +tcacheが枯渇すると、その後の解放は未整列ビンに移動し、古典的なファーストフィットの動作(テイルサーチ、ヘッド挿入)が再びトリガーされる可能性があります。 + +--- +### 🚩 ファーストフィットを使用したオーバーラップチャンクUAFの作成 + +以下のフラグメント(glibc 2.38でテスト済み)は、未整列ビンのスプリッタを悪用して2つの**オーバーラッピングポインタ**を作成する方法を示しています。これは、単一の解放を解放後書き込みに変換する強力なプリミティブです。 +```c +#include +#include +#include + +int main(){ +setbuf(stdout, NULL); + +/* 1. prepare 2 adjacent chunks and free the first one */ +char *A = malloc(0x420); // big enough to bypass tcache +char *B = malloc(0x420); +strcpy(A, "AAAA\n"); +free(A); // A → unsorted + +/* 2. request a *smaller* size to force a split of A */ +char *C = malloc(0x400); // returns lower half of former A + +/* 3. The remainder of A is still in the unsorted bin. +Another 0x400-byte malloc will now return the *same* +region pointed to by B – creating a UAF/overlap. */ +char *C2 = malloc(0x400); + +printf("B = %p\nC2 = %p (overlaps B)\n", B, C2); + +// Arbitrary write in B is immediately visible via C2 +memset(B, 'X', 0x10); +fwrite(C2, 1, 0x10, stdout); // prints Xs +} +``` +Exploitation recipe (common in recent CTFs): + +1. **ターゲットサイズのtcacheを排出**します。 +2. **チャンクを解放**して、未ソートのビンに配置します。 +3. **わずかに小さいサイズを割り当て**ます – アロケータが未ソートのチャンクを分割します。 +4. **再度割り当て**ます – 残りの部分が既存の使用中のチャンクと重なります → UAF。 +5. 機密フィールド(関数ポインタ、FILE vtableなど)を上書きします。 + +実用的なアプリケーションは、2024 HITCON Quals *Setjmp* チャレンジに見られ、この正確なプリミティブがUAFから`__free_hook`の完全制御にピボットするために使用されます。{{#ref}} +../../../../references/2024_setjmp_firstfit.md +{{#endref}} + +--- +### 🛡️ 緩和策と強化 + +* **セーフリンク(glibc ≥ 2.32)**は、単一リンクの*tcache*/**fastbin**リストのみを保護します。未ソート/小/大ビンは依然として生のポインタを格納しているため、ヒープリークを取得できればファーストフィットに基づく重なりが有効です。 +* **ヒープポインタの暗号化とMTE**(ARM64)は、まだx86-64 glibcには影響しませんが、`GLIBC_TUNABLES=glibc.malloc.check=3`のようなディストリビューションの強化フラグは、一貫性のないメタデータで中止し、単純なPoCを壊す可能性があります。 +* **解放時のtcacheの充填**(2024年にglibc 2.41のために提案)は、未ソートの使用をさらに減少させるでしょう。一般的なエクスプロイトを開発する際は、今後のリリースを監視してください。 + +--- ## その他の参考文献と例 - [**https://heap-exploitation.dhavalkapil.com/attacks/first_fit**](https://heap-exploitation.dhavalkapil.com/attacks/first_fit) - [**https://8ksec.io/arm64-reversing-and-exploitation-part-2-use-after-free/**](https://8ksec.io/arm64-reversing-and-exploitation-part-2-use-after-free/) -- ARM64. Use after free: ユーザーオブジェクトを生成し、それを解放し、解放されたチャンクを取得して書き込むオブジェクトを生成し、**前のユーザーのuser->passwordの位置を上書き**します。ユーザーを再利用して**パスワードチェックをバイパス**します。 +- ARM64. Use after free: ユーザーオブジェクトを生成し、それを解放し、解放されたチャンクを取得するオブジェクトを生成して書き込むことを許可し、**前のものからuser->passwordの位置を上書き**します。ユーザーを再利用して**パスワードチェックをバイパス**します。 - [**https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/use_after_free/#example**](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/use_after_free/#example) -- プログラムはノートを作成することを許可します。ノートには、malloc(8)内にノート情報があり(呼び出すことができる関数へのポインタ付き)、ノートの内容を持つ別のmalloc(\)へのポインタがあります。 -- 攻撃は、ノート情報サイズよりも大きなmalloc内容を持つ2つのノート(note0とnote1)を作成し、それらを解放してファストビン(またはtcache)に入れることです。 -- 次に、内容サイズ8の別のノート(note2)を作成します。内容はnote1にあり、チャンクが再利用されるため、関数ポインタをwin関数を指すように変更し、note1をUse-After-Freeして新しい関数ポインタを呼び出します。 +- プログラムはノートを作成することを許可します。ノートには、malloc(8)内のノート情報(呼び出すことができる関数へのポインタを含む)と、ノートの内容を持つ別のmalloc()へのポインタがあります。 +- 攻撃は、ノート情報サイズよりも大きいmalloc内容を持つ2つのノート(note0とnote1)を作成し、それらを解放してファーストビン(またはtcache)に入れることです。 +- 次に、内容サイズ8の別のノート(note2)を作成します。内容はnote1にあり、チャンクが再利用されるため、関数ポインタをwin関数を指すように変更し、その後note1をUse-After-Freeして新しい関数ポインタを呼び出します。 - [**https://guyinatuxedo.github.io/26-heap_grooming/pico_areyouroot/index.html**](https://guyinatuxedo.github.io/26-heap_grooming/pico_areyouroot/index.html) -- メモリを割り当て、希望の値を書き込み、それを解放し、再割り当てすることが可能です。前のデータがまだ存在するため、新しい期待される構造に従って処理され、値を設定してフラグを取得することが可能になります。 +- メモリを割り当て、希望の値を書き込み、解放し、再割り当てすることが可能で、以前のデータがまだそこにあるため、新しい期待される構造に従って扱われ、値を設定してフラグを取得することが可能になります。 - [**https://guyinatuxedo.github.io/26-heap_grooming/swamp19_heapgolf/index.html**](https://guyinatuxedo.github.io/26-heap_grooming/swamp19_heapgolf/index.html) -- この場合、特定のチャンク内に4を書き込む必要があります。これは、すべてを強制的に解放した後でも最初に割り当てられるものです。新しく割り当てられた各チャンクの配列インデックス内の番号が保存されます。次に、4つのチャンク(最初に割り当てられたものを含む)を割り当て、最後のチャンクには4が含まれ、これらを解放し、最初のものの再割り当てを強制します。これにより、最後に解放されたチャンクが使用され、その中に4が含まれます。 +- この場合、特定のチャンク内に4を書き込む必要があります。これは、すべてを強制的に解放した後でも最初に割り当てられたものです。新しく割り当てられた各チャンクの配列インデックス内の番号が保存されます。次に、4つのチャンク(最初に割り当てられたものを含む)を割り当て、最後のものには4が含まれ、解放して最初のものの再割り当てを強制します。これにより、最後に解放されたチャンクが使用され、その中に4が含まれます。 +- 2024 HITCON Quals Setjmpの書き込み(Quarkslab) – 実用的なファーストフィット/未ソートスプリット重なり攻撃: +- Angstrom CTF 2024 *heapify*の書き込み – 未ソートビンの分割を悪用してlibcをリークし、重なりを得る: {{#include ../../../banners/hacktricks-training.md}}