0x00 前言
通过学习《macOS Control Bypasses》,这里对于dylib注入的方法做列举和总结,输出一个文档。以下内容不一定准确,欢迎大家提建议,发表你的看法。
0x01 Dylib注入方式
1x01 DYLD_INSERT_LIBRARIES 注入
通过设置环境变量 DYLD_INSERT_LIBRARIES 来在程序启动前注入dylib,该方法存在一定限制:
使用方法:DYLD_INSERT_LIBRARIES=example.dylib ./hello
1x02 Dylib Hijacking
利用动态加载器(dyld)加载共享库时的特性进行注入,限制条件同上。
此时就存在两种场景:
1x03 Dylib Proxying
dylib劫持的另一种利用方式,LC_REEXPORT_DYLIB可以以代{过}{滤}理的方式加载dylib,导出相关的函数,由此可以在编译注入的dylib时,通过重命名原始dylib并让自己的dylib指向真实dylib,将被劫持的dylib以代{过}{滤}理模式重新导出,避免程序崩溃,限制条件同上。
// 编译dylib,适配current_version和compatibility_version,这个具体的值需要自行更改
// -reexport_library 是以代{过}{滤}理的方式加载存在的dylib,防止程序崩溃,也就是LC_REEXPORT_DYLIB
gcc -dynamiclib -current_version 1.0 -compatibility_version 1.0 -framework Foundation hijack.m -Wl,-reexport_library,"hijacked_file_path" -o hijack.dylib
// 默认情况代{过}{滤}理加载的dylib是以@rpath的形式加载的,修改为绝对路径的形式
install_name_tool -change @rpath/xxxx.dylib "/xxx/xxx/hijacked_file_path" hijack.dylib
1x04 dlopen劫持
当应用使用dlopen函数且未指定完整路径时,dyld会搜索不同路径。
搜索路径顺序:
限制条件:
1x05 Mach Task Port注入
Mach是macOS的微内核,负责基本任务如调度、线程管理、硬件接口、虚拟内存和进程间通信,使用task作为资源共享的最小单位,一个task可以包含多个线程,通信通过单向通道和端口(port)进行,存在一个特殊的端口是task port,获得task port的SEND权限可以完全控制目标进程,可以读写虚拟内存,创建/停止线程等。
task port访问限制:
开启SIP
如果应用有com.apple.security.get-task-allow权限,同用户级别的进程可以访问其task port
有com.apple.system-task-ports权限可以访问任何进程的task port(仅限Apple自身应用)
非Apple自身应用且未启用hardened runtime时,root可以获取其task port
注入shellcode步骤:
注入dylib步骤:
关键代码示例(不能直接拿来用):
// 1. 获取目标进程的task port
task_t remoteTask;
kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask);
// 2. 在远程进程分配内存
mach_vm_address_t remoteStack64;
mach_vm_address_t remoteCode64;
kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
kr = mach_vm_allocate(remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE);
// 3. 写入shellcode
kr = mach_vm_write(remoteTask, remoteCode64, (vm_offset_t)shellcode, CODE_SIZE);
// 4. 设置内存权限
kr = vm_protect(remoteTask, remoteCode64, CODE_SIZE, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
// 5. 创建远程线程执行shellcode
thread_act_t remoteThread;
x86_thread_state64_t remoteThreadState64;
thread_create_running(remoteTask, x86_THREAD_STATE64,
(thread_state_t)&remoteThreadState64,
x86_THREAD_STATE64_COUNT,
&remoteThread);
0x02 注入维持思路
insert_dylib InsertDylib
环境变量注入方式
在Info.plist文件中添加环境变量信息
LSEnvironment
DYLD_INSERT_LIBRARIES
/Applications/xxxx.app/Contents/xxxx.dylib
此方法需要移除程序的签名,需要刷新Info.plist的缓存
codesign --remove-signature /Applications/xxxx.app/Contents/MacOS/xxxx
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/ Support/lsregister -f /Application/xxxx.app
Dylib劫持或者代{过}{滤}理方式
0x03 总结
SIP开启情况下,最好的方法就是insert_dylib和环境变量维持的方式,但是需要删除签名,在某些存在校验的应用上会无效,还有就是如果有程序自带帮助程序/系统扩展的XPC服务,有可能会部分功能失效,因为XPC服务可能会校验请求的客户端(如果没有校验那它有被XPC攻击的可能,那你研究研究没准儿能搞出个CVE)。
SIP关闭,那你随意玩耍,注入基本就不是大问题了,此状态下也可以试试frida,frida的原理应该就是通过进程注入的方式在程序中注入javascript引擎,然后再用进程间通信机制来操作执行代码。