hacktricks/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-library-injection
2023-08-03 19:12:22 +00:00
..
README.md Translated to Chinese 2023-08-03 19:12:22 +00:00

macOS库注入

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥

{% hint style="danger" %} dyld的代码是开源的,可以在https://opensource.apple.com/source/dyld/找到,并且可以使用URL例如https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz下载tar文件。 {% endhint %}

DYLD_INSERT_LIBRARIES

这是一个以冒号分隔的动态库列表用于在程序指定的库之前加载。这使您可以通过加载一个临时的动态共享库其中只包含新模块来测试用于平面命名空间映像中使用的现有动态共享库的新模块。请注意这对使用动态共享库构建的二级命名空间映像没有任何影响除非还使用了DYLD_FORCE_FLAT_NAMESPACE。

这类似于Linux上的LD_PRELOAD

这种技术也可以用作ASEP技术因为每个安装的应用程序都有一个名为"Info.plist"的plist文件允许使用名为LSEnvironmental的键来分配环境变量。

{% hint style="info" %} 自2012年以来Apple已大大降低了DYLD_INSERT_LIBRARIES的权限。

转到代码并检查src/dyld.cpp。在函数**pruneEnvironmentVariables中,您可以看到DYLD_***变量被删除。

在函数**processRestricted**中,设置了限制的原因。检查该代码,您可以看到原因是:

  • 二进制文件是setuid/setgid
  • 在macho二进制文件中存在__RESTRICT/__restrict部分。
  • 软件具有没有com.apple.security.cs.allow-dyld-environment-variables权限或com.apple.security.cs.disable-library-validation权限的权限(硬化运行时)。
  • 使用以下命令检查二进制文件的权限:codesign -dv --entitlements :- </path/to/bin>
  • 如果库与二进制文件使用不同的证书签名
  • 如果库和二进制文件使用相同的证书签名,这将绕过先前的限制
  • 具有权限**system.install.apple-softwaresystem.install.apple-software.standar-user**的程序可以在不要求用户输入密码的情况下安装由Apple签名的软件特权升级

在更新的版本中,您可以在函数**configureProcessRestrictions的第二部分找到此逻辑。但是,在较新的版本中执行的是函数的开始检查**您可以删除与iOS或模拟相关的if语句因为这些在macOS中不会使用。 {% endhint %}

您可以使用codesign --display --verbose <bin>检查二进制文件是否具有硬化运行时,并检查**CodeDirectory中的标志运行时,例如:CodeDirectory v=20500 size=767 flags=0x10000(runtime) hashes=13+7 location=embedded**

在以下位置找到有关如何(滥用)使用此功能并检查限制的示例:

{% content-ref url="../../macos-dyld-hijacking-and-dyld_insert_libraries.md" %} macos-dyld-hijacking-and-dyld_insert_libraries.md {% endcontent-ref %}

Dylib劫持

{% hint style="danger" %} 请记住,先前的限制也适用于执行Dylib劫持攻击。 {% endhint %}

与Windows一样在MacOS中您也可以劫持dylib以使应用程序执行任意代码
然而MacOS应用程序加载库的方式比Windows更加受限制。这意味着恶意软件开发人员仍然可以使用这种技术进行隐蔽,但是滥用此技术以提升权限的可能性要低得多。

首先,更常见的是发现MacOS二进制文件指示加载库的完整路径。其次,MacOS从不在$PATH的文件夹中搜索库

与此功能相关的主要代码部分位于ImageLoader.cpp中的**ImageLoader::recursiveLoadLibraries**中。

然而,有2种类型的dylib劫持

  • 缺少弱链接库:这意味着应用程序将尝试加载一个使用LC_LOAD_WEAK_DYLIB配置的不存在的库。然后,如果攻击者将dylib放在预期位置它将被加载
  • 链接是"weak"的事实意味着即使找不到库,应用程序也将继续运行。
  • 与此相关的代码位于ImageLoaderMachO.cppImageLoaderMachO::doGetDependentLibraries函数中,当LC_LOAD_WEAK_DYLIB为true时lib->required仅为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`

{% hint style="info" %}
**`@executable_path`**:是包含**主可执行文件**的目录的**路径**。

**`@loader_path`**:是包含包含加载命令的 Mach-O 二进制文件的**目录**的**路径**。

* 在可执行文件中使用时,**`@loader_path`** 实际上与 **`@executable_path`** 相同。
* 在 **dylib** 中使用时,**`@loader_path`** 给出了 **dylib** 的路径。
{% endhint %}

滥用此功能进行**提权**的方式是,在**以 root 身份执行的应用程序**中,寻找某个**库**位于攻击者具有写权限的某个文件夹中的**罕见情况**。

{% hint style="success" %}
一个很好的用于查找应用程序中**缺失库**的扫描器是 [**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)找到。
{% endhint %}

**示例**

{% content-ref url="../../macos-dyld-hijacking-and-dyld_insert_libraries.md" %}
[macos-dyld-hijacking-and-dyld\_insert\_libraries.md](../../macos-dyld-hijacking-and-dyld\_insert\_libraries.md)
{% endcontent-ref %}

### Dlopen Hijacking

来自 **`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/`** 中查找。
1. `$DYLD_LIBRARY_PATH`
2. `LC_RPATH`
3. `CWD`(如果无限制)
4. `$DYLD_FALLBACK_LIBRARY_PATH`
5. `/usr/local/lib/`(如果无限制)
6. `/usr/lib/`
* 当路径**看起来像是框架路径**(例如 /stuff/foo.framework/foo如果在启动时设置了 **`$DYLD_FRAMEWORK_PATH`**dyld 将首先在该目录中查找框架的部分路径(例如 foo.framework/foo。接下来dyld 将尝试**使用提供的路径**对于相对路径使用当前工作目录。最后对于旧的二进制文件dyld 将尝试一些回退。如果在启动时设置了 **`$DYLD_FALLBACK_FRAMEWORK_PATH`**dyld 将在这些目录中搜索。否则,它将在 **`/Library/Frameworks`**(在 macOS 上,如果进程是无限制的)中搜索,然后在 **`/System/Library/Frameworks`** 中搜索。
1. `$DYLD_FRAMEWORK_PATH`
2. 提供的路径(对于相对路径,使用当前工作目录)
3. `$DYLD_FALLBACK_FRAMEWORK_PATH`(如果无限制)
4. `/Library/Frameworks`(如果无限制)
5. `/System/Library/Frameworks`
* 当路径**包含斜杠但不是框架路径**时(即完整路径或 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/`

注意:如果主可执行文件是一个**set\[ug]id 二进制文件或带有授权签名**,则**所有环境变量都会被忽略**,只能使用完整路径。

**检查路径**

让我们使用以下代码检查所有选项:
```c
#include <dlfcn.h>
#include <stdio.h>

int main(void)
{
void* handle;

handle = dlopen("just_name_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n", dlerror());
}

handle = dlopen("a/framework/rel_framework_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n", dlerror());
}

handle = dlopen("/a/abs/framework/abs_framework_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n", dlerror());
}

handle = dlopen("a/folder/rel_folder_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n", dlerror());
}

handle = dlopen("/a/abs/folder/abs_folder_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n", dlerror());
}

return 0;
}

如果您编译并执行它,您可以看到每个库未成功搜索的位置。此外,您可以过滤文件系统日志

sudo fs_usage | grep "dlopentest"
☁️ HackTricks 云 ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥