hacktricks/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-library-injection
2023-09-24 14:35:53 +00:00
..
README.md Translated ['README.md', 'backdoors/salseo.md', 'cryptography/certificat 2023-09-24 14:35:53 +00:00

macOS库注入

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

{% hint style="danger" %} dyld的代码是开源的,可以在https://opensource.apple.com/source/dyld/找到,并且可以使用类似https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz的URL下载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**中。

Macho二进制文件可以使用4个不同的头命令来加载库:

  • **LC_LOAD_DYLIB**命令是加载dylib的常见命令。
  • **LC_LOAD_WEAK_DYLIB**命令与前一个命令类似但如果找不到dylib则继续执行而不会出现任何错误。
  • **LC_REEXPORT_DYLIB**命令代理(或重新导出)来自不同库的符号。
  • **LC_LOAD_UPWARD_DYLIB**命令在两个库相互依赖时使用这称为_向上依赖_

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

  • 缺少弱链接库:这意味着应用程序将尝试加载一个配置了LC_LOAD_WEAK_DYLIB的不存在的库。然后,如果攻击者将一个dylib放在预期的位置它将被加载
  • 链接是“弱链接”的意思是,即使找不到库,应用程序也会继续运行。
  • 与此相关的代码位于ImageLoaderMachO.cpp文件的ImageLoaderMachO::doGetDependentLibraries函数中,当LC_LOAD_WEAK_DYLIB为true时lib->required只有在false时才为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劫持

来自**`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/`

{% hint style="danger" %}
如果名称中没有斜杠,有两种方法可以进行劫持:

* 如果任何**`LC_RPATH`**是**可写的**(但是签名会被检查,所以为此还需要二进制文件是无限制的)
* 如果二进制文件是**无限制的**然后可以从CWD加载某些内容或滥用上述环境变量之一
{% endhint %}

* 当路径**看起来像一个框架路径**时(例如`/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`

{% hint style="danger" %}
如果是框架路径,劫持的方式是:

* 如果进程是**无限制的**滥用CWD的**相对路径**和上述环境变量即使在文档中没有提到如果进程受限制DYLD\_\*环境变量会被删除)
{% endhint %}

* 当路径**包含斜杠但不是框架路径**时即完整路径或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/`

{% hint style="danger" %}
如果名称中有斜杠而不是框架,则劫持它的方法是:

* 如果二进制文件是**无限制的**则可以从CWD或`/usr/local/lib`加载内容(或滥用其中一个环境变量)
{% endhint %}

{% hint style="info" %}
注意:**没有**配置文件来**控制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平台上大多数操作系统dylib都**合并到dyld缓存中**,并且不存在于磁盘上。因此,调用**`stat()`**来预先检查操作系统dylib是否存在**不起作用**。但是,**`dlopen_preflight()`**使用与**`dlopen()`**相同的步骤来查找兼容的mach-o文件。
{% endhint %}

**检查路径**

让我们使用以下代码检查所有选项:
```c
// 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;
}

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

sudo fs_usage | grep "dlopentest"

删除DYLD_*LD_LIBRARY_PATH环境变量

在文件dyld-dyld-832.7.1/src/dyld2.cpp中,可以找到函数**pruneEnvironmentVariables,它将删除任何以DYLD_LD_LIBRARY_PATH=**开头的环境变量。

对于suidsgid二进制文件,它还会将环境变量**DYLD_FALLBACK_FRAMEWORK_PATHDYLD_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;

这基本上意味着,如果二进制文件是suidsgid,或者在头部有一个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

The __RESTRICT section is a segment in macOS that is used to restrict access to certain libraries and prevent unauthorized processes from injecting code into them. This section is specifically designed to enhance the security of macOS by preventing privilege escalation attacks through library injection.

The __restrict segment, on the other hand, is a specific area within the __RESTRICT section that contains code and data that are restricted from modification or injection. This segment is heavily protected by macOS to ensure the integrity and security of the libraries it contains.

By leveraging the __RESTRICT section and the __restrict segment, macOS can effectively mitigate the risks associated with library injection attacks, making it more difficult for malicious actors to exploit vulnerabilities and gain unauthorized access to sensitive system resources.

__RESTRICT部分与__restrict

__RESTRICT部分是macOS中的一个段用于限制对某些库的访问并防止未经授权的进程向其注入代码。该部分专门设计用于增强macOS的安全性通过防止通过库注入进行权限提升攻击。

另一方面,__restrict段是__RESTRICT部分中的一个特定区域其中包含受限制的代码和数据禁止进行修改或注入。macOS对该段进行了严格保护以确保其中包含的库的完整性和安全性。

通过利用__RESTRICT部分和__restrictmacOS可以有效地减轻与库注入攻击相关的风险使恶意行为者更难利用漏洞并未授权地访问敏感系统资源。

gcc -sectcreate __RESTRICT __restrict /dev/null hello.c -o hello-restrict
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-restrict

强化运行时

在钥匙串中创建一个新的证书,并使用它对二进制文件进行签名:

{% code overflow="wrap" %}

# Apply runtime proetction
codesign -s <cert-name> --option=runtime ./hello
DYLD_INSERT_LIBRARIES=inject.dylib ./hello #Library won't be injected

# Apply library validation
codesign -f -s <cert-name> --option=library ./hello
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed #Will throw an error because signature of binary and library aren't signed by same cert (signs must be from a valid Apple-signed developer certificate)

# 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 <cert-name> inject.dylib
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed

# Apply CS_RESTRICT protection
codesign -f -s <cert-name> --option=restrict hello-signed
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed # Won't work

{% endcode %}

{% hint style="danger" %} 请注意,即使有用标志**0x0(none)签名的二进制文件,当执行时它们也可以动态地获得CS_RESTRICT**标志,因此这种技术在它们上面不起作用。

您可以使用以下命令检查进程是否具有此标志(获取csops here

csops -status <pid>

然后检查是否启用了标志位0x800。 {% endhint %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥