浅谈Dylib注入

查看 98|回复 9
作者:Soft98   
Dylib Injection
0x00 前言
通过学习《macOS Control Bypasses》,这里对于dylib注入的方法做列举和总结,输出一个文档。以下内容不一定准确,欢迎大家提建议,发表你的看法。
0x01 Dylib注入方式
  • 环境变量注入
  • DYLD_INSERT_LIBRARIES 注入
  • 动态注入
  • Dylib Hijacking(动态库劫持)
  • Dylib Proxying(动态库代{过}{滤}理)
  • dlopen劫持
  • 进程注入
  • Mach Task Port注入


    1x01 DYLD_INSERT_LIBRARIES 注入
    通过设置环境变量 DYLD_INSERT_LIBRARIES 来在程序启动前注入dylib,该方法存在一定限制:
  • 对于设置了SUID位的二进制文件无效
  • 包含__RESTRICT/__restrict分段的二进制文件无效
  • 开启SIP状态并签名的情况下
  • 对启用Hardened Rutime/CS_RESTRICT的应用无效
  • 对启用了library validation的应用无效
  • 注入的dylib需要有正确的签名
  • 如果包含以下两种entitlement可以注入
  • com.apple.security.cs.allow-dyld-environment-variables
  • com.apple.security.cs.disable-library-validation


    使用方法:DYLD_INSERT_LIBRARIES=example.dylib ./hello
    1x02 Dylib Hijacking
    利用动态加载器(dyld)加载共享库时的特性进行注入,限制条件同上。
  • LC_LOAD_WEAK_DYLIB命令在加载动态库时如果找不到文件会继续运行,不会抛出异常
  • LC_RPATH会指定搜索目录,可以定义多个
  • LC_LOAD_DYLIB/LC_LOAD_WEAK_DYLIB/LC_LOAD_UPWARD_DYLIB指定的路径如果是@rpath/xxx.dylib,程序在加载时会按顺序依次在LC_PATH中搜寻


    此时就存在两种场景:
  • 应用使用LC_LOAD_WEAK_DYLIB命令但实际dylib不存在时,可以放置自己的dylib
  • @rpath搜索路径顺序中,如果前面的路径不存在dylib,可以在前面路径放置自己的dylib

    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会搜索不同路径。
    搜索路径顺序:
  • $LD_LIBRARY_PATH (如果设置)
  • $DYLD_LIBRARY_PATH (如果设置)
  • 当前工作目录
  • $DYLD_FALLBACK_LIBRARY_PATH (如果设置)
  • $DYLD_FALLBACK_LIBRARY_PATH (如果未设置)
  • $HOME/lib
  • /usr/local/lib
  • /usr/lib
  • 当前目录


    限制条件:
  • 如果二进制文件设置了 setuid/setgid 位或者签名包含 entitlements,所有环境变量都会被忽略
  • 对于受限制的二进制文件,只能使用完整路径调用 dlopen
  • /usr/lib 目录受 SIP 保护,即使 root 用户也无法写入

    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步骤:
  • 使用task_for_pid获取目标进程task port的SEND权限
  • 在目标进程分配内存并写入shellcode
  • 创建远程线程执行shellcode

    注入dylib步骤:
  • 需要先将Mach线程提升为POSIX线程
  • 使用pthread_create_from_mach_thread创建线程
  • 使用dlopen加载dylib
  • 处理线程的双重特性(Mach和POSIX)

    关键代码示例(不能直接拿来用):
    // 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
  • 这两款工具本质上一样的,后者是论坛@QiuChenly在前者的基础上增加了一些功能
  • 工具会在被注入的程序上插入一个LC_LOAD_DYLIB指令,修复相关数据,让程序在运行时主动去加载指定的dylib
  • 如果加上--weak会插入LC_LOAD_WEAK_DYLIB指令
  • 检测到签名也可以移除签名

  • 环境变量注入方式

  • 在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引擎,然后再用进程间通信机制来操作执行代码。

    线程, 进程

  • fanqie01   

    虽然我没看懂,但是巨魔用户注入是真的香啊。
    MrYuxuan   

    感谢分享,谢谢啦
    LPCQQ120828   

    看不懂系列,但是感觉楼主挺牛逼
    massle   

    以前没接触过,看起来挺牛的,但是我看不懂。
    liyuanfangdrj   

    感谢分享
    Bruce_HD   

    支持支持,不错的分享。
    kongson   

    这个不错,下载测试一下,先谢谢楼主了
    braumhuang   

    感谢分享,一直在用TrollFools
    csdoc   

    感谢分享,mark
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部