macOS Library Injection
Reading time: 21 minutes
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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
caution
dyldのコードはオープンソースであり、https://opensource.apple.com/source/dyld/で見つけることができ、URLのようなものでtarとしてダウンロードできます:https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz
Dyldプロセス
Dyldがバイナリ内でライブラリをどのようにロードするかを確認してください:
DYLD_INSERT_LIBRARIES
これはLinuxのLD_PRELOADのようなものです。特定のライブラリをパスからロードするために実行されるプロセスを指定することができます(環境変数が有効な場合)。
この技術は、すべてのインストールされたアプリケーションに「Info.plist」と呼ばれるplistがあり、LSEnvironmental
というキーを使用して環境変数を割り当てることができるため、ASEP技術としても使用される可能性があります。
note
2012年以降、AppleはDYLD_INSERT_LIBRARIES
の権限を大幅に制限しました。
コードを確認し、**src/dyld.cpp
をチェックしてください。関数pruneEnvironmentVariables
では、DYLD_*
**変数が削除されることがわかります。
関数**processRestricted
**では、制限の理由が設定されています。そのコードを確認すると、理由は次のとおりです:
- バイナリが
setuid/setgid
である - machoバイナリに
__RESTRICT/__restrict
セクションが存在する - ソフトウェアが
com.apple.security.cs.allow-dyld-environment-variables
権限なしに権限を持っている(ハードンされたランタイム)- バイナリの権限を確認するには:
codesign -dv --entitlements :- </path/to/bin>
- バイナリの権限を確認するには:
より新しいバージョンでは、このロジックは関数**configureProcessRestrictions
の後半に見つけることができます。ただし、新しいバージョンで実行されるのは関数の最初のチェック**です(iOSやシミュレーションに関連するifを削除できます。これらはmacOSでは使用されません)。
ライブラリの検証
バイナリが**DYLD_INSERT_LIBRARIES
**環境変数の使用を許可していても、バイナリがライブラリの署名をチェックする場合、カスタムライブラリはロードされません。
カスタムライブラリをロードするには、バイナリが次のいずれかの権限を持っている必要があります:
com.apple.security.cs.disable-library-validation
com.apple.private.security.clear-library-validation
または、バイナリはハードンされたランタイムフラグまたはライブラリ検証フラグを持っていない必要があります。
バイナリがハードンされたランタイムを持っているかどうかは、codesign --display --verbose <bin>
を使用して、CodeDirectory
内のフラグruntimeを確認できます:CodeDirectory v=20500 size=767 flags=0x10000(runtime) hashes=13+7 location=embedded
バイナリと同じ証明書で署名されたライブラリをロードすることもできます。
この方法を(悪用)する方法と制限を確認するには:
macOS Dyld Hijacking & DYLD_INSERT_LIBRARIES
Dylibハイジャック
caution
以前のライブラリ検証制限もDylibハイジャック攻撃を実行するために適用されることを忘れないでください。
Windowsと同様に、MacOSでもdylibsをハイジャックしてアプリケーションが任意のコードを実行することができます(実際には、通常のユーザーからは、.app
バンドル内に書き込むためにTCC権限が必要なため、これは不可能かもしれません)。
ただし、MacOSアプリケーションがライブラリをロードする方法はWindowsよりも制限されています。これは、マルウェア開発者がこの技術をステルスに使用できることを意味しますが、権限を昇格させるために悪用できる可能性ははるかに低いです。
まず第一に、MacOSバイナリがライブラリをロードするための完全なパスを示すことがより一般的です。第二に、MacOSはライブラリのために$PATHのフォルダを検索することはありません。
この機能に関連するコードの主な部分は、ImageLoader.cpp
の**ImageLoader::recursiveLoadLibraries
**にあります。
machoバイナリがライブラリをロードするために使用できる4つの異なるヘッダーコマンドがあります:
- **
LC_LOAD_DYLIB
**コマンドはdylibをロードするための一般的なコマンドです。 - **
LC_LOAD_WEAK_DYLIB
**コマンドは前のコマンドと同様に機能しますが、dylibが見つからない場合、実行はエラーなしで続行されます。 - **
LC_REEXPORT_DYLIB
**コマンドは、異なるライブラリからシンボルをプロキシ(または再エクスポート)します。 - **
LC_LOAD_UPWARD_DYLIB
**コマンドは、2つのライブラリが互いに依存している場合に使用されます(これは_上向き依存関係_と呼ばれます)。
ただし、dylibハイジャックには2種類あります:
- 欠落している弱リンクライブラリ:これは、アプリケーションがLC_LOAD_WEAK_DYLIBで構成された存在しないライブラリをロードしようとすることを意味します。次に、攻撃者が期待される場所にdylibを配置すると、それがロードされます。
- リンクが「弱い」ということは、ライブラリが見つからなくてもアプリケーションは実行を続けることを意味します。
- これに関連するコードは、
ImageLoaderMachO::doGetDependentLibraries
のImageLoaderMachO.cpp
の関数にあり、lib->required
はLC_LOAD_WEAK_DYLIB
がtrueのときのみfalse
です。 - バイナリ内の弱リンクライブラリを見つけるには(後でハイジャックライブラリを作成する方法の例があります):
-
otool -l </path/to/bin> | grep LC_LOAD_WEAK_DYLIB -A 5 cmd LC_LOAD_WEAK_DYLIB cmdsize 56 name /var/tmp/lib/libUtl.1.dylib (offset 24) time stamp 2 Wed Jun 21 12:23:31 1969 current version 1.0.0 compatibility version 1.0.0
- **@rpathで構成された**:Mach-Oバイナリは**`LC_RPATH`**および**`LC_LOAD_DYLIB`**コマンドを持つことができます。これらのコマンドの**値**に基づいて、**ライブラリ**は**異なるディレクトリ**から**ロード**されます。
- **`LC_RPATH`**は、バイナリによってライブラリをロードするために使用されるいくつかのフォルダのパスを含みます。
- **`LC_LOAD_DYLIB`**は、ロードする特定のライブラリへのパスを含みます。これらのパスには**`@rpath`**が含まれる場合があり、これは**`LC_RPATH`**の値によって**置き換えられます**。**`LC_RPATH`**に複数のパスがある場合、すべてがライブラリをロードするために使用されます。例:
- **`LC_LOAD_DYLIB`**が`@rpath/library.dylib`を含み、**`LC_RPATH`**が`/application/app.app/Contents/Framework/v1/`および`/application/app.app/Contents/Framework/v2/`を含む場合。両方のフォルダが`library.dylib`をロードするために使用されます。**ライブラリが`[...] /v1/`に存在しない場合、攻撃者はそこに配置して`[...] /v2/`のライブラリのロードをハイジャックできます。** **`LC_LOAD_DYLIB`**のパスの順序が守られます。
- バイナリ内の**rpathパスとライブラリを見つける**には:`otool -l </path/to/binary> | grep -E "LC_RPATH|LC_LOAD_DYLIB" -A 5`
> [!NOTE] > **`@executable_path`**:は**メイン実行可能ファイル**を含むディレクトリへの**パス**です。
>
> **`@loader_path`**:は**ロードコマンドを含むMach-Oバイナリ**を含む**ディレクトリ**への**パス**です。
>
> - 実行可能ファイルで使用されるとき、**`@loader_path`**は実質的に**`@executable_path`**と同じです。
> - **dylib**で使用されるとき、**`@loader_path`**は**dylib**への**パス**を提供します。
この機能を悪用して**権限を昇格させる**方法は、**root**によって実行されている**アプリケーション**が、攻撃者が書き込み権限を持つフォルダ内の**ライブラリを探している**という稀なケースにあります。
<div class="mdbook-alerts mdbook-alerts-tip">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
tip
</p>
アプリケーション内の**欠落しているライブラリ**を見つけるための優れた**スキャナー**は、[**Dylib Hijack Scanner**](https://objective-see.com/products/dhs.html)または[**CLIバージョン**](https://github.com/pandazheng/DylibHijack)です。\
この技術に関する**技術的詳細を含む優れたレポート**は[**こちら**](https://www.virusbulletin.com/virusbulletin/2015/03/dylib-hijacking-os-x)で見つけることができます。
</div>
**例**
<a class="content_ref" href="macos-dyld-hijacking-and-dyld_insert_libraries.md"><span class="content_ref_label">macOS Dyld Hijacking & DYLD_INSERT_LIBRARIES</span></a>
## Dlopenハイジャック
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
**以前のライブラリ検証制限もDlopenハイジャック攻撃を実行するために適用されることを忘れないでください**。
</div>
**`man dlopen`**から:
- パスに**スラッシュ文字が含まれていない場合**(つまり、単なるリーフ名の場合)、**dlopen()は検索を行います**。**`$DYLD_LIBRARY_PATH`**が起動時に設定されている場合、dyldは最初にそのディレクトリを**探します**。次に、呼び出し元のmach-oファイルまたはメイン実行可能ファイルが**`LC_RPATH`**を指定している場合、dyldは**それらの**ディレクトリを**探します**。次に、プロセスが**制限されていない**場合、dyldは**現在の作業ディレクトリ**を検索します。最後に、古いバイナリの場合、dyldはいくつかのフォールバックを試みます。**`$DYLD_FALLBACK_LIBRARY_PATH`**が起動時に設定されている場合、dyldは**それらのディレクトリ**を検索します。そうでない場合、dyldは**`/usr/local/lib/`**(プロセスが制限されていない場合)を検索し、次に**`/usr/lib/`**を検索します(この情報は**`man dlopen`**から取得されました)。
1. `$DYLD_LIBRARY_PATH`
2. `LC_RPATH`
3. `CWD`(制限されていない場合)
4. `$DYLD_FALLBACK_LIBRARY_PATH`
5. `/usr/local/lib/`(制限されていない場合)
6. `/usr/lib/`
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
名前にスラッシュがない場合、ハイジャックを行う方法は2つあります:
- いずれかの**`LC_RPATH`**が**書き込み可能**である場合(ただし署名がチェックされるため、これにはバイナリが制限されていない必要があります)
- バイナリが**制限されていない**場合、CWDから何かをロードすることが可能です(または前述の環境変数のいずれかを悪用することができます)
</div>
- パスが**フレームワークのように見える**場合(例:`/stuff/foo.framework/foo`)、**`$DYLD_FRAMEWORK_PATH`**が起動時に設定されている場合、dyldは最初にそのディレクトリで**フレームワーク部分パス**(例:`foo.framework/foo`)を探します。次に、dyldは**指定されたパスをそのまま**試みます(相対パスの場合は現在の作業ディレクトリを使用)。最後に、古いバイナリの場合、dyldはいくつかのフォールバックを試みます。**`$DYLD_FALLBACK_FRAMEWORK_PATH`**が起動時に設定されている場合、dyldはそれらのディレクトリを検索します。そうでない場合、dyldは**`/Library/Frameworks`**(macOSでプロセスが制限されていない場合)、次に**`/System/Library/Frameworks`**を検索します。
1. `$DYLD_FRAMEWORK_PATH`
2. 指定されたパス(制限されていない場合は相対パスに現在の作業ディレクトリを使用)
3. `$DYLD_FALLBACK_FRAMEWORK_PATH`
4. `/Library/Frameworks`(制限されていない場合)
5. `/System/Library/Frameworks`
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
フレームワークパスの場合、ハイジャックする方法は次のとおりです:
- プロセスが**制限されていない**場合、CWDからの**相対パス**を悪用することができます。前述の環境変数(プロセスが制限されている場合、DYLD_*環境変数は削除されます)を使用します。
</div>
- パスが**スラッシュを含むがフレームワークパスでない場合**(つまり、dylibへの完全なパスまたは部分的なパス)、dlopen()は最初に(設定されている場合)**`$DYLD_LIBRARY_PATH`**(パスのリーフ部分を使用)を検索します。次に、dyldは**指定されたパスを試みます**(相対パスの場合は現在の作業ディレクトリを使用しますが、制限されていないプロセスの場合のみ)。最後に、古いバイナリの場合、dyldはいくつかのフォールバックを試みます。**`$DYLD_FALLBACK_LIBRARY_PATH`**が起動時に設定されている場合、dyldはそれらのディレクトリを検索します。そうでない場合、dyldは**`/usr/local/lib/`**(プロセスが制限されていない場合)を検索し、次に**`/usr/lib/`**を検索します。
1. `$DYLD_LIBRARY_PATH`
2. 指定されたパス(制限されていない場合は相対パスに現在の作業ディレクトリを使用)
3. `$DYLD_FALLBACK_LIBRARY_PATH`
4. `/usr/local/lib/`(制限されていない場合)
5. `/usr/lib/`
<div class="mdbook-alerts mdbook-alerts-caution">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
caution
</p>
名前にスラッシュがあり、フレームワークでない場合、ハイジャックする方法は次のとおりです:
- バイナリが**制限されていない**場合、CWDまたは`/usr/local/lib`から何かをロードすることが可能です(または前述の環境変数のいずれかを悪用することができます)。
</div>
<div class="mdbook-alerts mdbook-alerts-note">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
note
</p>
注意:**dlopen検索を制御するための**構成ファイルは**ありません**。
注意:メイン実行可能ファイルが**set\[ug]idバイナリまたは権限でコードサインされている場合**、**すべての環境変数は無視され**、フルパスのみが使用できます(詳細な情報については[DYLD_INSERT_LIBRARIES制限を確認してください](macos-dyld-hijacking-and-dyld_insert_libraries.md#check-dyld_insert_librery-restrictions))。
注意:Appleプラットフォームは、32ビットと64ビットのライブラリを組み合わせるために「ユニバーサル」ファイルを使用します。これは、**32ビットと64ビットの検索パスが別々に存在しない**ことを意味します。
注意:Appleプラットフォームでは、ほとんどのOS dylibが**dyldキャッシュに統合され**、ディスク上には存在しません。したがって、OS dylibが存在するかどうかを事前確認するために**`stat()`**を呼び出すことは**機能しません**。ただし、**`dlopen_preflight()`**は、互換性のあるmach-oファイルを見つけるために**`dlopen()`**と同じ手順を使用します。
</div>
**パスを確認する**
次のコードを使用してすべてのオプションを確認しましょう:
// gcc dlopentest.c -o dlopentest -Wl,-rpath,/tmp/test #include <dlfcn.h> #include <stdio.h>
int main(void) { void* handle;
fprintf("--- No slash ---\n"); handle = dlopen("just_name_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Relative framework ---\n"); handle = dlopen("a/framework/rel_framework_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Abs framework ---\n"); handle = dlopen("/a/abs/framework/abs_framework_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Relative Path ---\n"); handle = dlopen("a/folder/rel_folder_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
fprintf("--- Abs Path ---\n"); handle = dlopen("/a/abs/folder/abs_folder_dlopentest.dylib",1); if (!handle) { fprintf(stderr, "Error loading: %s\n\n\n", dlerror()); }
return 0; }
もしコンパイルして実行すると、**各ライブラリがどこで見つからなかったか**を見ることができます。また、**FSログをフィルタリングすることもできます**:
sudo fs_usage | grep "dlopentest"
## 相対パスハイジャック
特権バイナリ/アプリ(SUIDや強力な権限を持つバイナリなど)が相対パスライブラリ(例えば、`@executable_path`や`@loader_path`を使用)を読み込んでおり、ライブラリ検証が無効になっている場合、攻撃者が相対パスで読み込まれるライブラリを変更できる場所にバイナリを移動し、プロセスにコードを注入するためにそれを悪用することが可能です。
## `DYLD_*`および`LD_LIBRARY_PATH`環境変数の削除
ファイル`dyld-dyld-832.7.1/src/dyld2.cpp`には、**`pruneEnvironmentVariables`**という関数があり、**`DYLD_`**で始まる環境変数と**`LD_LIBRARY_PATH=`**を削除します。
また、特に**suid**および**sgid**バイナリに対して、環境変数**`DYLD_FALLBACK_FRAMEWORK_PATH`**と**`DYLD_FALLBACK_LIBRARY_PATH`**を**null**に設定します。
この関数は、OSXをターゲットにする場合、同じファイルの**`_main`**関数から呼び出されます。
#if TARGET_OS_OSX if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) { pruneEnvironmentVariables(envp, &apple);
そのブールフラグは、コード内の同じファイルに設定されています:
#if TARGET_OS_OSX // support chrooting from old kernel bool isRestricted = false; bool libraryValidation = false; // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) { isRestricted = true; } bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0); uint32_t flags; if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { // On OS X CS_RESTRICT means the program was signed with entitlements if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) { isRestricted = true; } // Library Validation loosens searching but requires everything to be code signed if ( flags & CS_REQUIRE_LV ) { isRestricted = false; libraryValidation = true; } } gLinkContext.allowAtPaths = !isRestricted; gLinkContext.allowEnvVarsPrint = !isRestricted; gLinkContext.allowEnvVarsPath = !isRestricted; gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP; gLinkContext.allowClassicFallbackPaths = !isRestricted; gLinkContext.allowInsertFailures = false; gLinkContext.allowInterposing = true;
バイナリが**suid**または**sgid**であるか、ヘッダーに**RESTRICT**セグメントがあるか、**CS_RESTRICT**フラグで署名されている場合、**`!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache`**が真となり、環境変数は削除されます。
CS_REQUIRE_LVが真である場合、変数は削除されませんが、ライブラリの検証はそれらが元のバイナリと同じ証明書を使用しているかどうかを確認します。
## 制限の確認
### SUID & SGID
Make it owned by root and suid
sudo chown root hello sudo chmod +s hello
Insert the library
DYLD_INSERT_LIBRARIES=inject.dylib ./hello
Remove suid
sudo chmod -s hello
### セクション `__RESTRICT` とセグメント `__restrict`
gcc -sectcreate __RESTRICT __restrict /dev/null hello.c -o hello-restrict DYLD_INSERT_LIBRARIES=inject.dylib ./hello-restrict
### Hardened runtime
Keychainで新しい証明書を作成し、それを使用してバイナリに署名します:
Apply runtime proetction
codesign -s
Apply library validation
codesign -f -s
Sign it
If the signature is from an unverified developer the injection will still work
If it's from a verified developer, it won't
codesign -f -s
Apply CS_RESTRICT protection
codesign -f -s