diff --git a/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md b/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md index 0914cb3c6..25b6a5cc0 100644 --- a/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md +++ b/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md @@ -47,12 +47,14 @@ Segment Sections... バイナリをメモリにロードするために使用するローダーのパスを示します。 +> ヒント: 静的リンクまたは静的-PIEバイナリには`INTERP`エントリがありません。その場合、動的ローダーは関与せず、それに依存する技術(例: `ret2dlresolve`)は無効になります。 + ### LOAD これらのヘッダーは**バイナリをメモリにロードする方法**を示すために使用されます。\ -各**LOAD**ヘッダーは**メモリ**の領域(サイズ、権限、アライメント)を示し、ELF **バイナリからコピーするバイト**を示します。 +各**LOAD**ヘッダーは**メモリ**の領域(サイズ、権限、アライメント)を示し、そこにコピーするELF**バイナリのバイト**を示します。 -例えば、2番目のものはサイズが0x1190で、0x1fc48に位置し、読み取りおよび書き込みの権限を持ち、オフセット0xfc48から0x528で埋められます(すべての予約されたスペースは埋めません)。このメモリにはセクション`.init_array .fini_array .dynamic .got .data .bss`が含まれます。 +例えば、2番目のものはサイズが0x1190で、0x1fc48に位置し、読み取りと書き込みの権限を持ち、オフセット0xfc48から0x528で埋められます(予約されたスペース全体は埋めません)。このメモリにはセクション`.init_array .fini_array .dynamic .got .data .bss`が含まれます。 ### DYNAMIC @@ -62,21 +64,35 @@ Segment Sections... これはバイナリに関するベンダーメタデータ情報を保存します。 +- x86-64では、`readelf -n`は`.note.gnu.property`内の`GNU_PROPERTY_X86_FEATURE_1_*`フラグを表示します。`IBT`および/または`SHSTK`が表示される場合、バイナリはCET(間接分岐追跡および/またはシャドウスタック)でビルドされています。これはROP/JOPに影響を与え、間接分岐ターゲットは`ENDBR64`命令で始まる必要があり、リターンはシャドウスタックに対してチェックされます。詳細とバイパスノートについてはCETページを参照してください。 + +{{#ref}} +../common-binary-protections-and-bypasses/cet-and-shadow-stack.md +{{#endref}} + ### GNU_EH_FRAME -スタックアンワインドテーブルの位置を定義し、デバッガーやC++例外処理ランタイム関数によって使用されます。 +デバッガーやC++例外処理ランタイム関数によって使用されるスタックアンワインドテーブルの位置を定義します。 ### GNU_STACK -スタック実行防止防御の構成を含みます。これが有効な場合、バイナリはスタックからコードを実行できません。 +スタック実行防止防御の設定を含みます。これが有効な場合、バイナリはスタックからコードを実行できません。 + +- `readelf -l ./bin | grep GNU_STACK`で確認してください。テスト中に強制的に切り替えるには、`execstack -s|-c ./bin`を使用できます。 ### GNU_RELRO -バイナリのRELRO(Relocation Read-Only)構成を示します。この保護は、プログラムがロードされた後、実行を開始する前に、メモリの特定のセクション(`GOT`や`init`および`fini`テーブルなど)を読み取り専用としてマークします。 +バイナリのRELRO(再配置読み取り専用)設定を示します。この保護は、プログラムがロードされた後、実行を開始する前に、メモリの特定のセクション(`GOT`や`init`および`fini`テーブルなど)を読み取り専用としてマークします。 -前の例では、0x3b8バイトを0x1fc48に読み取り専用としてコピーし、セクション`.init_array .fini_array .dynamic .got .data .bss`に影響を与えています。 +前の例では、0x3b8バイトを0x1fc48に読み取り専用としてコピーし、セクション`.init_array .fini_array .dynamic .got .data .bss`に影響を与えます。 -RELROは部分的または完全であることに注意してください。部分的なバージョンは、**遅延バインディング**に使用され、ライブラリのアドレスを最初に検索する際にこのメモリスペースに**書き込み権限**が必要なセクション**`.plt.got`**を保護しません。 +RELROは部分的または完全である可能性があり、部分的なバージョンは**`.plt.got`**セクションを保護せず、これは**レイジーバインディング**に使用され、ライブラリのアドレスを最初に検索する際に**書き込み権限**を持つこのメモリスペースが必要です。 + +> エクスプロイト技術と最新のバイパスノートについては、専用ページを確認してください: + +{{#ref}} +../common-binary-protections-and-bypasses/relro.md +{{#endref}} ### TLS @@ -145,11 +161,11 @@ CONTENTS, READONLY 25 .gnu_debuglink 00000034 0000000000000000 0000000000000000 000101bc 2**2 CONTENTS, READONLY ``` -それは、位置、オフセット、権限だけでなく、そのセクションの**データの種類**も示しています。 +それは、位置、オフセット、権限だけでなく、そのセクションが持つ**データの種類**も示しています。 ### メタセクション -- **文字列テーブル**: ELFファイルに必要なすべての文字列を含んでいます(ただし、プログラムで実際に使用されるものは含まれていません)。例えば、`.text`や`.data`のようなセクション名が含まれています。そして、もし`.text`が文字列テーブルのオフセット45にある場合、**name**フィールドには番号**45**が使用されます。 +- **文字列テーブル**: ELFファイルに必要なすべての文字列を含んでいます(ただし、プログラムで実際に使用されるものは含まれていません)。例えば、`.text`や`.data`のようなセクション名が含まれています。そして、もし`.text`が文字列テーブルのオフセット45にある場合、**name**フィールドには数字**45**が使用されます。 - 文字列テーブルの場所を見つけるために、ELFは文字列テーブルへのポインタを含んでいます。 - **シンボルテーブル**: 名前(文字列テーブルのオフセット)、アドレス、サイズ、シンボルに関するその他のメタデータなど、シンボルに関する情報を含んでいます。 @@ -160,11 +176,11 @@ CONTENTS, READONLY - **`.bss`**: 初期化されていないグローバル変数(またはゼロに初期化)。ここにある変数は自動的にゼロに初期化されるため、バイナリに無駄なゼロが追加されるのを防ぎます。 - **`.rodata`**: 定数グローバル変数(読み取り専用セクション)。 - **`.tdata`**および**`.tbss`**: スレッドローカル変数が使用されるときの.dataおよび.bssのようなもの(C++の`__thread_local`またはCの`__thread`)。 -- **`.dynamic`**: 下記を参照。 +- **`.dynamic`**: 以下を参照してください。 ## シンボル -シンボルは、関数、グローバルデータオブジェクト、スレッドローカル変数など、プログラム内の名前付きの位置です。 +シンボルは、プログラム内の名前付きの場所で、関数、グローバルデータオブジェクト、スレッドローカル変数などである可能性があります。 ``` readelf -s lnstat @@ -188,12 +204,16 @@ Num: Value Size Type Bind Vis Ndx Name 各シンボルエントリには以下が含まれます: - **名前** -- **バインディング属性**(弱い、ローカル、またはグローバル):ローカルシンボルはプログラム自体によってのみアクセス可能ですが、グローバルシンボルはプログラムの外部で共有されます。弱いオブジェクトは、例えば異なる関数によってオーバーライド可能な関数です。 +- **バインディング属性**(weak, local または global):ローカルシンボルはプログラム自身によってのみアクセス可能ですが、グローバルシンボルはプログラムの外部で共有されます。weakオブジェクトは、例えば異なるもので上書き可能な関数です。 - **タイプ**:NOTYPE(タイプ指定なし)、OBJECT(グローバルデータ変数)、FUNC(関数)、SECTION(セクション)、FILE(デバッガ用のソースコードファイル)、TLS(スレッドローカル変数)、GNU_IFUNC(再配置用の間接関数) -- **セクション** インデックス(位置) +- **セクション** インデックス(その位置) - **値**(メモリ内のアドレス) - **サイズ** +#### GNUシンボルバージョニング(dynsym/dynstr/gnu.version) + +現代のglibcはシンボルバージョンを使用します。`.gnu.version`や`.gnu.version_r`にエントリが表示され、シンボル名は`strlen@GLIBC_2.17`のようになります。動的リンカーはシンボルを解決する際に特定のバージョンを要求することがあります。手動で再配置を作成する際(例:ret2dlresolve)には、正しいバージョンインデックスを提供しなければならず、そうでないと解決に失敗します。 + ## 動的セクション ``` readelf -d lnstat @@ -229,11 +249,28 @@ Tag Type Name/Value 0x000000006ffffff9 (RELACOUNT) 15 0x0000000000000000 (NULL) 0x0 ``` -NEEDEDディレクトリは、プログラムが**言及されたライブラリをロードする必要がある**ことを示しています。NEEDEDディレクトリは、共有**ライブラリが完全に動作し、使用可能になる**と完了します。 +NEEDEDディレクトリは、プログラムが**言及されたライブラリをロードする必要がある**ことを示しています。NEEDEDディレクトリは、共有**ライブラリが完全に動作可能で使用準備が整った**ときに完了します。 -## リロケーション +### ダイナミックローダーの検索順序 (RPATH/RUNPATH, $ORIGIN) -ローダーは、依存関係をロードした後にそれらをリロケートする必要があります。これらのリロケーションは、リロケーションテーブルのRELまたはRELA形式で示され、リロケーションの数は動的セクションのRELSZまたはRELASZで示されます。 +エントリ`DT_RPATH`(非推奨)および/または`DT_RUNPATH`は、ダイナミックローダーが依存関係を検索する場所に影響を与えます。おおよその順序は次のとおりです: + +- `LD_LIBRARY_PATH`(setuid/sgidまたはその他の「セキュア実行」プログラムでは無視される) +- `DT_RPATH`(`DT_RUNPATH`が存在しない場合のみ) +- `DT_RUNPATH` +- `ld.so.cache` +- `/lib64`、`/usr/lib64`などのデフォルトディレクトリ + +`$ORIGIN`は、RPATH/RUNPATH内でメインオブジェクトのディレクトリを参照するために使用できます。攻撃者の視点からは、ファイルシステムのレイアウトや環境を制御する場合に重要です。ハードニングされたバイナリ(AT_SECURE)では、ほとんどの環境変数はローダーによって無視されます。 + +- 確認方法:`readelf -d ./bin | egrep -i 'r(path|unpath)'` +- クイックテスト:`LD_DEBUG=libs ./bin 2>&1 | grep -i find`(検索パスの決定を表示) + +> Priv-escのヒント:あなたが所有する書き込み可能なRUNPATHや誤設定された`$ORIGIN`相対パスを悪用することを好むべきです。LD_PRELOAD/LD_AUDITはセキュア実行(setuid)コンテキストでは無視されます。 + +## 再配置 + +ローダーは、依存関係をロードした後にそれらを再配置する必要があります。これらの再配置は、RELまたはRELA形式の再配置テーブルに示され、再配置の数は動的セクションのRELSZまたはRELASZに示されます。 ``` readelf -r lnstat @@ -274,7 +311,6 @@ Offset Info Type Sym. Value Sym. Name + Addend 00000001fea0 000900000402 R_AARCH64_JUMP_SL 0000000000000000 perror@GLIBC_2.17 + 0 00000001fea8 000b00000402 R_AARCH64_JUMP_SL 0000000000000000 __cxa_finalize@GLIBC_2.17 + 0 00000001feb0 000c00000402 R_AARCH64_JUMP_SL 0000000000000000 putc@GLIBC_2.17 + 0 -00000001feb8 000d00000402 R_AARCH64_JUMP_SL 0000000000000000 opendir@GLIBC_2.17 + 0 00000001fec0 000e00000402 R_AARCH64_JUMP_SL 0000000000000000 fputc@GLIBC_2.17 + 0 00000001fec8 001100000402 R_AARCH64_JUMP_SL 0000000000000000 snprintf@GLIBC_2.17 + 0 00000001fed0 001200000402 R_AARCH64_JUMP_SL 0000000000000000 __snprintf_chk@GLIBC_2.17 + 0 @@ -308,13 +344,13 @@ Offset Info Type Sym. Value Sym. Name + Addend ``` ### 静的再配置 -もし**プログラムが好ましいアドレス**(通常は0x400000)とは異なる場所にロードされる場合、アドレスがすでに使用されているか、**ASLR**やその他の理由によるもので、静的再配置は**ポインタを修正**します。これらのポインタは、バイナリが好ましいアドレスにロードされることを期待していた値を持っています。 +もし**プログラムが好ましいアドレス**(通常は0x400000)とは異なる場所にロードされる場合、アドレスがすでに使用されているか、**ASLR**やその他の理由によるものであれば、静的再配置は**ポインタを修正**します。これにより、バイナリが好ましいアドレスにロードされることを期待していた値を持つポインタが修正されます。 例えば、`R_AARCH64_RELATIV`型の任意のセクションは、再配置バイアスに加算値を加えたアドレスを修正する必要があります。 ### 動的再配置とGOT -再配置は外部シンボル(依存関係からの関数など)を参照することもあります。例えば、libCからのmalloc関数です。次に、ローダーはlibCをアドレスにロードする際、malloc関数がロードされている場所を確認し、そのアドレスをGOT(グローバルオフセットテーブル)テーブルに書き込みます(再配置テーブルで示されている)mallocのアドレスが指定されるべき場所です。 +再配置は外部シンボル(依存関係からの関数など)を参照することもあります。例えば、libCからのmalloc関数です。この場合、ローダーはlibCをアドレスにロードする際に、malloc関数がロードされている場所を確認し、そのアドレスをGOT(グローバルオフセットテーブル)に書き込みます(再配置テーブルで示されている)mallocのアドレスが指定されるべき場所です。 ### プロシージャリンクテーブル @@ -322,9 +358,27 @@ PLTセクションは遅延バインディングを実行することを可能 したがって、プログラムがmallocを呼び出すと、実際にはPLT内の`malloc`の対応する位置(`malloc@plt`)を呼び出します。最初に呼び出されたときに`malloc`のアドレスを解決し、それを保存するので、次回`malloc`が呼び出されると、そのアドレスがPLTコードの代わりに使用されます。 -## プログラム初期化 +#### 攻撃に影響を与える現代のリンク動作 -プログラムがロードされた後、実行する時間です。しかし、最初に実行されるコードは**必ずしも`main`**関数ではありません。これは、例えばC++では**グローバル変数がクラスのオブジェクト**である場合、このオブジェクトはmainが実行される前に**初期化**されなければならないからです。 +- `-z now`(フルRELRO)は遅延バインディングを無効にします。PLTエントリは存在しますが、GOT/PLTは読み取り専用にマッピングされるため、**GOT上書き**や**ret2dlresolve**のような技術はメインバイナリに対しては機能しません(ライブラリは部分的にRELROである可能性があります)。参照: + +{{#ref}} +../common-binary-protections-and-bypasses/relro.md +{{#endref}} + +- `-fno-plt`はコンパイラが**GOTエントリを直接**介して外部関数を呼び出すようにします。これにより、`call func@plt`の代わりに`mov reg, [got]; call reg`のような呼び出しシーケンスが見られます。これにより、投機的実行の悪用が減少し、PLTスタブ周辺のROPガジェットハンティングがわずかに変わります。 + +- PIEと静的PIE:PIE(`INTERP`を持つET_DYN)は動的ローダーを必要とし、通常のPLT/GOT機構をサポートします。静的PIE(`INTERP`を持たないET_DYN)はカーネルローダーによって再配置が適用され、`ld.so`はありません。ランタイムでのPLT解決は期待できません。 + +> GOT/PLTが選択肢でない場合は、他の書き込み可能なコードポインタにピボットするか、libcへの古典的なROP/SROPを使用してください。 + +{{#ref}} +../arbitrary-write-2-exec/aw2exec-got-plt.md +{{#endref}} + +## プログラムの初期化 + +プログラムがロードされた後、実行する時間です。しかし、最初に実行されるコードは**必ずしも`main`**関数ではありません。これは、例えばC++では**グローバル変数がクラスのオブジェクト**である場合、このオブジェクトはmainが実行される前に**初期化**されなければならないためです。 ```cpp #include // g++ autoinit.cpp -o autoinit @@ -354,9 +408,19 @@ __attributte__((destructor)) //Add to the destructor list ``` コンパイラの観点から、`main` 関数が実行される前後にこれらのアクションを実行するために、`init` 関数と `fini` 関数を作成することが可能で、これらは動的セクションで **`INIT`** と **`FIN`** として参照され、ELF の `init` および `fini` セクションに配置されます。 -他のオプションとして、**`INIT_ARRAY`** および **`FINI_ARRAY`** エントリの **`__CTOR_LIST__`** および **`__DTOR_LIST__`** リストを参照することが挙げられ、これらの長さは **`INIT_ARRAYSZ`** および **`FINI_ARRAYSZ`** によって示されます。各エントリは引数なしで呼び出される関数ポインタです。 +他のオプションとして、**`INIT_ARRAY`** および **`FINI_ARRAY`** エントリの中で **`__CTOR_LIST__`** と **`__DTOR_LIST__`** のリストを参照することが挙げられ、これらの長さは **`INIT_ARRAYSZ`** と **`FINI_ARRAYSZ`** によって示されます。各エントリは引数なしで呼び出される関数ポインタです。 -さらに、**`INIT_ARRAY`** ポインタの **前に** 実行される **ポインタ** を持つ **`PREINIT_ARRAY`** を持つことも可能です。 +さらに、**`PREINIT_ARRAY`** を持つことも可能で、これは **`INIT_ARRAY`** ポインタの **前に** 実行される **ポインタ** です。 + +#### 攻撃メモ + +- Partial RELRO の下では、これらの配列は `ld.so` が `PT_GNU_RELRO` を読み取り専用に切り替える前にまだ書き込み可能なページに存在します。十分早く任意の書き込みを取得するか、ライブラリの書き込み可能な配列をターゲットにすることができれば、選択した関数でエントリを上書きすることで制御フローをハイジャックできます。Full RELRO の下では、実行時に読み取り専用です。 + +- 動的リンカーの遅延バインディングを悪用して、実行時に任意のシンボルを解決する方法については、専用ページを参照してください: + +{{#ref}} +../rop-return-oriented-programing/ret2dlresolve.md +{{#endref}} ### 初期化順序 @@ -369,7 +433,7 @@ __attributte__((destructor)) //Add to the destructor list ## スレッドローカルストレージ (TLS) -C++ ではキーワード **`__thread_local`** または GNU 拡張 **`__thread`** を使用して定義されます。 +これは C++ で **`__thread_local`** キーワードを使用して定義されるか、GNU 拡張の **`__thread`** を使用して定義されます。 各スレッドはこの変数のユニークな場所を維持するため、スレッドのみがその変数にアクセスできます。 @@ -379,4 +443,31 @@ C++ ではキーワード **`__thread_local`** または GNU 拡張 **`__thread` `__TLS_MODULE_BASE` はスレッドローカルストレージのベースアドレスを参照するために使用されるシンボルで、モジュールのすべてのスレッドローカルデータを含むメモリ内の領域を指します。 +## 補助ベクター (auxv) と vDSO + +Linux カーネルは、実行時に役立つアドレスやフラグを含む補助ベクターをプロセスに渡します: + +- `AT_RANDOM`: glibc がスタックカナリアや他の PRNG シードに使用する 16 バイトのランダムデータを指します。 +- `AT_SYSINFO_EHDR`: vDSO マッピングのベースアドレス(`__kernel_*` システムコールやガジェットを見つけるのに便利)。 +- `AT_EXECFN`、`AT_BASE`、`AT_PAGESZ` など。 + +攻撃者として、もし `/proc` の下でメモリやファイルを読み取ることができれば、ターゲットプロセスでの情報漏洩なしにこれらを漏洩させることができます: +```bash +# Show the auxv of a running process +cat /proc/$(pidof target)/auxv | xxd + +# From your own process (helper snippet) +#include +#include +int main(){ +printf("AT_RANDOM=%p\n", (void*)getauxval(AT_RANDOM)); +printf("AT_SYSINFO_EHDR=%p\n", (void*)getauxval(AT_SYSINFO_EHDR)); +} +``` +`AT_RANDOM`を漏洩させることで、そのポインタを参照できればカナリア値を取得できます;`AT_SYSINFO_EHDR`は、ガジェットを探すためのvDSOベースを提供したり、直接高速システムコールを呼び出すために使用できます。 + +## References + +- ld.so(8) – ダイナミックローダーの検索順序、RPATH/RUNPATH、セキュア実行ルール (AT_SECURE): https://man7.org/linux/man-pages/man8/ld.so.8.html +- getauxval(3) – 補助ベクタとAT_*定数: https://man7.org/linux/man-pages/man3/getauxval.3.html {{#include ../../banners/hacktricks-training.md}}