IOS 应用介绍
IOS 应用是 Xcode 和 iOS SDK 在 Mac OSX 计算机上开发的。开发后的应用,在发送给苹果公司检查后,批准后签上苹果的私钥。
iOS 必须有受信任一方前面,否则 iOS 的强制性代码签名(Mandatory Code-Signing)会让程序无法在设备上运行
防漏洞攻击
- 数据执行保护
- 内存虚拟化
- 权限分离
- 沙箱让某些流程无法接触敏感数据
安全框架
更小的受攻击面
减小受攻击面,iOS中 不支持 Java 和 Flash
且 iOS 不能处理一些 MAC OSX 可以处理的文件。
如:Safari 和 MobileSafari。 .mov 在 iOS 和 Mac OS X。PDF , iOS原生支持.pdf文件,但是只解析改文件格式的部分特性。
精简的 iOS
iOS 下没有 shell (/bin/sh)
权限分离
有的应用运行是利用 用户 mobile 身份
系统进程利用 特权用户 root 身份
从而让存在漏洞的 mobile 用户应用能执行的代码被限制。
代码签名
需要 受信任机构的签名,才能被内核允许执行。
从而让应用不能动态改变行为或者完成自身升级。
如果系统越狱了,未签名的应用可以在越狱过的设备上安全运行。
数据执行保护
DEP 不允许数据执行,只允许代码执行
可以使用 ROP 技术绕过 DEP (类似 linux pwn 的操作)
代码签名,要求对应页 必须页源自受信任机构签名,否者该页不会被执行
地址空间布局随机化
利用 ROP 绕过 DEP 这里我们需要得到 代码段位置,
利用 ASLR (地址空间布局随机化)让 二进制文件,库文件,动态链接文件,栈和堆内存位置全部随机
沙箱
iOS 防守机制最后一环。
通过沙箱,恶意攻击可能窃取设备上所有的照片和地址簿,但是它没办法执行发送短信等会直接使用话费的操作。
企业中的 iOS
利用 iPhone Configuration(iPhone配置实用工具),Lion Server Profile Manager(描述文件管理器)
为iOS 设备创建和应用配置描述文件,用于确保设备严格执行组织的安全政策。
iOS 配置管理
通过创建和安装配置描述文件进行管理。
只有通过 配置描述文件进行配置的设置,才能集中管理。
移动配置描述文件
这个文件是 XML 属性 property list
数据值是 base64 编码储存。
iPhone 配字实用工具
iPhone 配字实用工具
会自动在用户的 keychain 中 自动创建 根CA 证书,iPhone 配置实用工具会为那些通过USB 端口连接到运行他的主机上的设备自动创建证书。这个CA证书用于给 这些证书签名,创建证书是为了安全传输这些设备上的配置描述文件签名和加密。
移动设备管理
利用 MDM (移动设备管理)
是企业完成可以远程管理这些设备
MDM 网络通信
iOS 设备在接收到 推送通知时就会与配置够的MDM服务器直接简历简介。
iOS 设备本身与位于 courier.push.apple.com
的某一 APNS 信使服务器保持持久连接,其中APNS 信使服务器是所有 iOS 推送通知都要使用的集中通信通道。
不通过的链接方式 链接的要求也不一样
Lion Server 描述文件管理器
这是一种可作为 MDM API 服务器和管理控制台使用的 Ruby-on-Rails Web 应用
大多数管理任务都是在用 Web 浏览器链接到 Web应用描述文件管理器后进行
加密
IOS 保护静态数据(data-at-rest)的主要措施,Data Protection API
4位屏锁被猜到后,iOS 设备上利用 Data Protection API 加密的所有数据都可以被解密。
数据保护
开发者会用定义了文件和 keychain 项的保护等级的常量来标记它们。
不同的保护等级是通过 密钥分级
实现的
密钥分级的根部是 UID密钥
嵌入在板载的加密加速器中,无法通过软件访问。 用户的密码
加速器可以使用该秘钥加密或解密指定的数据。
设备被解锁后,用户的密码会由修改过的 PBKDF2 算法加密多次生成密码密钥(保存在内存中,直到设备再次被锁定。
UID 密钥会被加密后生成 设备密钥,用于表示与文件相关的各保护等级的密钥加密。
对数据保护的攻击
开发者充分使用 Data Protection API 保护敏感信息
对用户密码的攻击
利用标准 PBKDF2算法的修改版
利用用户密码 生成 密码密钥。
PBKDF2算法的修改版
使用带 UID 密钥的 AES加密。因为 UID 密钥是设备本身得出的,防止攻击者离线破解密码。
根据 CPU 速度,PBKDF2算法的修改版
的迭代加密次数是可变的。
从而是得输出密码后 会有一个延迟。
攻击使用工具。
当 iOS 磁盘镜像挂接到 Mac OS X 上,我们可以查看文件系统并查看所有的文件元数据,但是文件内容都是已加密的数据,想要查看文件数据,我们需要系统 keybag
解密文件系统中所有加密后的文件
也可以利用 引导自定义ramdisk 完整的获取取证数据。
代码签名和内存保护
利用 Mandatory Code Signing(强制代码签名)
严格控制iOS设备上执行代码。
得到许可的组织必须为每一个要在 iOS设备上运行的应用签名。内核只允许被前面的代码在设备上执行,不管是 设备出厂时预装的应用还是 App Store 下载安装的,都需要经过苹果公司私钥的签名。
强制代码签名,影响二进制文件,影响所有代码,包括库文件,内存可执行代码。
但是 Web浏览器 MobileSafari 是计时编译(Just In Time) 编译。
代码签名安全特性:
可以防御恶意软件进入 iOS 设备。
可以防御漏洞攻击。
类似于 微软的
DEP
防御,代码签名可以防止代码(shellcode)被注入到受影响的进程中执行。但是可以利用 ROP 关闭DEP
防护 再去执行 shellcode强制代码签名,iOS中 攻击者不能关闭强制代码签名,只能使用 ROP 不能使用 shellcode。
强制访问控制
强制代码签名机制大部分由 MACF (强制访问控制框架)控制。
MACF 继承自 FreeBSD
FreeBSD 包含了某些强制访问控制策略的实验性支持,还包含用于内核安全性扩展的框架(TrustedBSD MAC Framework)
iOS 只注册了两项策略 AMFI 和 沙箱
AMFI 钩子
苹果移动设备文件完整性
*授权的工作原理
验证授权文件的有效性
可以在 dyld_shared_cache
中找到的 libmis
动态库中的 MISProvisioningProfileCheckValidity
函数验证
- 签名证书必须由
Apple iPhone Certificate Authority
(苹果iPhone 证书管理机构)签发 - 签名证书的名称必须由
Apple iPhone OS Provisioning Profile Signing
- 证书的前面链不能长于3个链接
- 根证书必须具有特定的SHA1 散列值
- 描述文件的版本号一定是1
- 设备的UDID 一定要出现,或者该描述文件必须包含
ProvisionsAllDevices 键
- 描述文件一定没有过期
代码签名的实施方法
代码签名实际发生早起内核的虚拟内存系统,系统会检查独立的内存页以及已经作为整体的进程,看看它们是否起源于经过签名的代码
收集和验证签名信息
加载可执行代码时,内核会检测代码是否含有 LC_CODE_SIGNATURE
装载命令存储在一起的代码签名。
利用的函数是 XNU的 bsd/kern/mach_loader.c
parse_machfile
-> load_code_signature
-> ubc_cs_blob_add
-> vnode_check_signature
AMFI 在挂钩函数 vnode_check_signature
中执行实现的代码签名检查
确保已签名页不发生改变
代码签名机制必须持续地执行,才能防止已签名的代码被篡改,防止新代码被注入,并防止其他破坏。iOS通过不允许出现可执行和可写入页面满足这一需求。
动态代码签名
即时编译(JIT)因为严格的代码签名策略从而不能被使用。
即时编译能够很快的运行,所以在 iOS 4.3 中 引入了 动态代码签名的概念。允许即时编译
只允许特定进程创建可写且可执行的内存区域来执行即时编译工作
只能创建一块这样的区域。
破坏代码签名机制
内核对 mmap 中的 MAP_JIT
标记的检查机制存在漏洞。
漏洞存在位置代码
如果 MAP_FIXED
为0 我们的 flags & MAP_JIT
就没有检测的意义了。因为会一直为0。
如果 MAP_JIT MAP_PRIVATE MAP_FILE
标志都已设置,这一检测就没办法阻止调用 mmap了。
这一任何没有 RWX
权限的区域 iOS 进程都可以执行 如下调用。
从而返回一个 任意大小的 可读可写可执行的区域。
沙盒
iOS 提供了多层漏洞攻击缓解机制。
DEP (数据执行保护)
ASLR (地址空间分布随机化)
iOS 沙盒机制
提供结构约束进程行为,从而限制代码执行后的行为。
例如:
一个程序的子程序是对内容渲染,那黑客攻击的时候回去攻击这个子程序。所以我们要限制他只接受输入的内容不访问其他内容,通过禁止该子系统打开其他文件、执行其他程序或使用网络,我们就限制了攻击者在代码执行后的行为。
沙盒认识
组成部分
一些用于初始化和配置沙盒的用户库函数
用来处理内核日志和存放预置配置的Mach 服务器
利用 TrustedBSD API 实施单独策略的内核扩展
为在实施过程中评估某些策略限制提供正则表达式引擎的内核扩展
沙箱被分割成了多个组件,比如位于/usr/libexec/sandboxd
目录中的userland daemon
,这是com.apple.security.sandbox
是kext (Kernel Extension)
,还有依赖于AppContainer.Framework
的AppSandbox
私有框架。
沙盒是基于应用程序的安装位置,而不是基于安装权限。
首先实施沙盒机制
调用 libSystem::sandbox_init
利用 libsandbox.dylib
库 把我们理解的策略定义转化为内核需要的二进制格式。
然后将这些 二进制格式 传递给 由 TrustedBSD子系统
处理的 mac_syscall系统调用中
TrustedBSD
把沙盒初始化请求传递给 Sandbox.kext 内核扩展
进行处理。
初始化完成后
TrustedBSD
层钩挂的很多函数调用经过 Sandbox.kext
实施策略。
Sandbox.kext
会从 AppleMatch.kext
引入函数 对 系统调用参数 和 策略规则 使用的模式进行正则表达式匹配
使用沙盒
sandbox_init
只是 sandbox_init_internal
的代理函数
sandbox_init_with_params 和 sandbox_init_with_extemsions
也是如此
为调用它的进程配置沙盒
参数 一个描述文件,一组标志 一个用于存储错误信息的输出参数
沙盒的实现
沙盒是由 内存 和 用户空间组件 构成的。
内存会暴露配置接口,害扮演 ”看门人“ 的角色。
它会检查进程请求的操作,并根据与进程关联的沙盒描述文件对这些操作请求进行评估。
理解用户空间库的实现
利用 dyldinfo
可以确定为 sandbox_init
符号链接的共享库是哪个
sandbox_init
是经由 libSystem
链接
需要分析系统库,需要从缓存里面提取,
- 可以解密固件包(IPSW)中的根文件系统镜像
- 可以在已经越狱的iPhone上复制改缓存
共享缓存位于 /System/Library/Caches/com.apple.dyld/dyld_shared_cache_armv7
开源提取工具 dyld_decache
可以提取出
/usr/lib/system/libsystem_sandbox.dylib
/usr/lib/system/libsystem_kernel.dylib
/usr/lib/libsandbox.1.dylib
其中 sandbox_init_internal
函数介绍
这个函数会把表示参数和扩展的字符串数组转化成 libsandbox
格式
动态调用了 libsandbox.1.dylib
调用解析函数 sandbox_creat_params,sandbox_set_param,sandbox_create_extensions,sandbox_add_extension
然后会更具我们传入的 flag
标志位在 libsandbox.1.dylib
动态加载不同函数。
深入内核
沙盒内核扩展 用的是 TrustedBSD策略扩展
TrustedBSD策略实现
用来在内核中实现可插入,可组合式访问控制策略的框架。
是由 内核的检查点和为响应这些事件注册策略锁使用的逻辑组成的
XNU
中实现 TrustedBSD
策略的内核代码 xnu-1699.24.8/security
接口通过 mac_policy.h
公开
在 iOS 的 mac_policy_ops
结构体,这个结构体给出了所有进入沙盒策略扩展的入口点,
其中有 用于配置进程的 mpo_policy_syscall函数
以及用来在允许操作前对操作进行验证的某一个 mpo_xxx_check_yyy
调用。
从用户空间处理配置
公å开给用户空间的接口是 xnu-1699.24.8/security/mac.h
中定义,并通过xnu-1699.24.8/bsd/kern/syscalls.master
公开的
libsandbox
中的所有用户空间函数最后都是调用系统调用
上面的 mac_syscall
调用时提供给策略扩展,让他们自行动态添加系统调用。第一个参数通过 mpc_name
选择策略扩展,第二个参数用来选择在策略中调用哪个子系统调用,最后的参数表示任何参数都可以传递给策略子系统调用。
不管当前进程是否已经初始化沙盒 TrusredBSD
函数都会被调用。
如果未初始化,大多数函数都会在不进行检查的情况下早早返回,这种情况下,sbx_cred_label_update_execve
首先会为已加载的可执行镜像计算路径,如果可执行镜像在 /private/var/mobile/Applications
下,那么内置的沙盒描述文件会被加载,而且处于上述目录下的路径会被作为扩展添加。该扩展可以启用用于所有AppStore 应用的同意容器描述文件。
iOS 应用进行模糊测试
模糊测试 (fuzz)
也称为 动态分析
基于变异技术(mutation-based)模糊测试
只需要几分钟 设置和运行。
选择一种有效输入,这种输入会进行随机修改。
iOS 是精简过的 Mac OS X 大部分代码是相同的,有一种找 IOS 漏洞的方法是在 Mac OS X 与之相同的代码中找 bug
iOS SDK 提供了 iOS 模拟器。
可以在 /Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app
找到模拟器的二进制文件。
iPhone 其实有两个处理器。一个是主CPU,叫作应用处理器。一个是副CPU,叫作基带处理器。
主CPU 用于运行 iOS 操作系统内核。副CPU(基带处理器)运行特制的实时操作系统
漏洞攻击
bug类漏洞
对象生存期漏洞
攻击策略
第一种
- 强制释放易受攻击的对象
- 用自己可以控制的内容替换这个对象
- 触发对象的使用,获得代码执行权限
第二种
- 易受攻击的对象第一次释放后,用一个合法的对象代替这个对象,因为存在二次释放漏洞,新创建的对象也会被释放
- 用自己能够控制的对象替换这个新创建的对象
- 让应用程序使用这个自己能控制的对象获取代码执行
过去通常是重写堆的元数据这样可以设置对应的chunk为 Iuse 从而再次free
还有重写应用程序持有的特有数据,因为堆块通常会对其数据结构的一致性进行检查,重写应用程序特有的数据往往需要确保溢出的缓存区与需要重写的缓冲区离得近。
理解iOS 系统自带的分配程序
iOS 的分配程序叫 magazine malloc
区域
magazine malloc
调用的区域 region
其中分为 Tiny
small
large
分配程序为 tiny,small
维护这 32 个freelist freelist
第1到第31freelist用于分配,而最后的列表用于两个或多个彼此邻近的对象被释放后结合成。
magazine malloc
程序分别为系统中存在的各个CPU 维护着单独的区域,从而让分配程序更容易调节区域大小
内存分配
首先magazine malloc
会更具确定那个区域合适。tiny small 是一样的。
tiny small
堆块被释放的时候 magazine malloc
会在一个名为 mag_last_free
的专用结构成员中保留该堆块的引用。如果事情的大小符合 mag_last_free
结构引用的堆块的大小,会把这个堆块返回给程序。
如果不通过会在空闲表中找到大小相同的堆块。如果不成功会去检查最后一个空闲表,最后一个空闲表存储合并的较大内存块。
如果非空,且内存大小等于大于申请的 堆块的大小,会将其中的内存块分为两个部分,一部分返回给调用程序,另一部分返回该空闲表。
如果 free list 中没有合适的内存区域。 magazine malloc
会调用 mmap() 分配一个新内存块。这一过程由内存分配请求未得到满足的线程执行。
lagre
对于lagre
不利用 32个空闲表,而是具有包含所有可用数据项的缓存。因此程序会先孕照具有合适大小的已分配内存页,如果没有找到,就会搜索更大的内存块,并对其进行分割,得到满足要求的一块,将另一部分返回可用内存列表
如果没有 就会调用 mmap() 分配
内存释放
tiny small
tiny small 会先放假 mag_last_free
如果这个 结构体之前就存在 free 的堆块引用,那么之前存在的堆块对按照3个步骤被移动到合适的free list 中
- 分配程序会检查对象是否可以 与 排在它前面的对象合并。
- 然后验证对象能否与排在它后面的对象合并
- 最后形成的 堆块会被放置到相应的位置
如果合并后的堆块大于 tiny 区域 这个对象会被放入最后的 空闲表。
当 tiny 区域 质保函释放后的 内存块,整个区域会被释放给系统。
large
对于 large 对象,如果对象大于某个 阈值,那么该对象就会立刻被释放给系统,否则做法与 tiny small 对象处理类似,对象会被放到专门的位置 large_entry_cache_newest
如果 large 对象缓存中有足够的 空间,如果缓存中数据项的数量没有 超过允许放置其中的最大元素数,处在最近位置的对象就会被放进去。
如果对象放入缓存大小变得太大,缓存中最旧的对象就会被删除。
iOS 内存分配
MallocScribble
所有已释放的内存填充 0x55MallocPreScribble
所有未初始化的内存填充 0xAAMallocStackLogging
记录内存块的完整历史地址和栈日志
Dtrace 查看系统自带分配程序分配和释放内存块的过程。
理解 TCMalloc
TCMalloc 处理大对象分配和小对象分配有两种不同的机制。
大对象由 Pageheap
程序管理,会直接被转给之前讨论过的底层操作系统自带的分配程序处理
小对象完全由 TCMalloc
处理。
大对象的分配和释放
在为大于用户设定的阈值 kMaxSize 的对象分配内存时。
页面级的分配程序 Pageheap
分配的范围(span) 一组连续的内存页
更具已分配的范围构成的双向链表,看看有没有合适的大小可供 TCMalloc
使用。
这个双向链表有两个范围,一类是可供使用的,一类是由 TCMalloc
分配但要返回给底层系统堆的。
分配
先找是否有可使用的未被标志已释放,那么它会直接返回。如果没有会找到一个更大的,分割后返回。
如果没有合适的范围提供,会向底层操作系统请求一组新的内存页,并将其分为两个内存对象
- 一个对象有着所有请求的大小
- 一个对象 打大小则是分配的内存页的总大小减去请求的分配所需的内存大小
释放
如果我们不在需要某一范围,会将其前驱范围,后继范围 或合两者合并起来,然后将得到的返回标记为已释放。最后依据若干个用户定义的参数,合并后得到的返回会由垃圾收集器返回个系统。
小对象的分配和释放
分配
每个正在运行的线程都具有自己专用的对象缓存和freelist。
freelist是分成若干分配类的双向链表。
小于 1024 字节的对象计算 (object_size + 7)/8
对于大于这个值的对象计算 (object_size + 127 + (120 << 7)) / 128
除了每个线程的缓存,还有中央缓存(所有线程共享)。
分配时 对应线程的freelist,如果没有合适的分配类,会去查看中央缓存。
释放
会返回到线程缓存的 freelist ,freelist超出用户自定义的参数会进行垃圾回收。
垃圾回收器会把线程缓存 freelist 中的未使用对象返回给中央缓存 freelist
对 ASLR 的挑战
ios 上的 ASLR 会随机排列储存在 dyld_shared_cache
中的所有库
如果应用程序支持与位置无关的代码,那么主可执行文件也可以随机排列
利用 Saffron
漏洞攻击重新利用 溢出打败了 ASLR
1 | typedef struct T1_DecoderRec_ { |
攻击者会读入包括 parse_callback
在内的若干指针。并把根据通过出站读操作了解的情况构造的ROP 有效载荷存储在 buildchar
成员中,最后重写 parse_callback
触发调用
面向返回的程序设计
ARM基础知识
一共有 16 个寄存器。 R0~R15
最后 3个寄存器具有特殊的值和名称。
R13 为SP 栈指针寄存器,R14 为LR 链接寄存器,R15 为PC 程序计数器
寄存器是通用的我们可以任意值移动到 R15(PC 程序计数器) 中并改变程序流。
ARM 模式下指令为 32位
Thumb 模式下 指令为 16位
如果执行的地址中最不重要的一位等于1,处理机就是要执行Thumb 模式,否者就是ARM模式。程序寄存器(CPSR)的T位为1且J位为0 执行Thumb代码。
iOS 调用约定
前面4个参数 使用通用寄存器 R0 到 R3 传递的。更多的参数是压入栈中。返回值存放在 R0 寄存器
修改程序运行逻辑
- 修改 R15(PC 程序计数器)
- 利用 B 指令,将 R15 修改为第一个 操作数指定地址。
- 想要返回紧随调用之后的指令,需要利用 BL(带链接的分支)指令。不会吧程序计数器置为第一个操作数指定的地址,还会吧返回的地址存储到 LR 寄存器中。
- 如果地址是在寄存器中,我们需要利用 BX 指令,不存储返回地址的情况下改变程序流程
- BLX 会执行作为第一个 操作数传递并存储在寄存器中的地址,并将返回的地址存储到 LR 寄存器中。
系统调用好处,他们可以在不用担心库加载地址和随机化的情况下进行系统调用。
不管程序的地址空间加载了什么库都可以进行系统调用。
系统调用的编号存储在 SDK /usr/include/sys/syscall.h
中。
在 ARM 中 系统调用编号存储在 R12寄存器中,调用的时候利用 汇编指令 SVC
ROP 介绍
ARM 指令是 2字节或者4字节对齐的
在 iPhone 中 系统库都存储在 dyld_shared_cache
这个巨大的缓存中。
要找到利用的指令 我们可以利用 Miscellanecous
工具
利用 dyld_decache -f libSystem
到处对应的 libSystem
iOS 中使用 ROP
- 第一种方法是 用ROP希尔整个有效载荷
- 利用ROP 吧两个不同的漏洞攻击程序(对于内核而言其中一个是远程的,一个是本地的)连接起来。通过完成这一工作,攻击者可以绕过用户空间的代码签名,并在内核空间或者用户空间执行普通的有效载荷。
- 攻击瞄准最新版的
MobileSafari
那么ROP有效载荷可以向为 JIT(即时编译) 代码保留的内存页面写入标准有效载荷。 多数JavaScript 引擎都有即时编译技术。
内核的调试和漏洞攻击
我们要获得一份二进制形式的iOS 内核附件, kernelcache.release.*
可以从 iOS 固件的 IPSW找到他。
二进制文件是 IMG3 格式的,是打包加密过的。
解密需要 解密密钥 和 xpwntools 工具
用于解密 IMG3 文件的解密密钥和AES初始化向量是存储在文件之中的,不是明文是设备的GID密钥加密过的,GID密钥是固化在涉笔的硬件中的无法被提取
内核调试
系统会把 全部内核严重错误日志文件收集到 /Library/Logs/CrashReporter/Panics
目录中
越狱设备可以直接访问。
如果未越狱,我们可以通过 MobileDevices
框架启动 lockdown
守护进程的 com.apple.crashreportmover
服务,把严总错误和崩溃的日志文件移动到 /var/mobile/Library/Logs/CrashReporter
目录下。取回文件利用 com.apple.crashreportcopymobile AFC
服务
内核 IOKit 扩展
IOKit 对象总是会扩展其他IOKit对象或由IOKit 基本对象派生的对象
要为每个 IOKit 对象注册一个元类,该元类揭示了该对象的名称以及指向其父对象的指针
元类的定义在 iOS 4的IOKit 驱动程序二进制文件中是紧随类定义之后的,对于 iOS 5来说则是在类定义的附近。
IOKit 驱动程序中寻找漏洞的过程与其他的内核扩展或内核本身之中寻找漏洞过程基本相同。不过IOKit 使用的 C++ 有多重漏洞类型
new 和 delete 使用不匹配,如果使用 delete[] 删除单个对象
对象的释放后使用漏洞
对象类型冲突的漏洞
如果针对 IOKit API 的漏洞攻击程序必须以非常类似上述内容的代码开头。因为绝大多数 IOKit驱动程序是闭源的。一旦用户空间的工具连接到设备,就有多种不同的方式可以把该连接用来与内部驱动程序通信。
属性与内核驱动程序的通信方式
设置属性
IOUserClient接口 可以直接的内存映射,外部trap和方法
通过设备属性进行攻击
第一条可能的攻击途径是改变与设备关联的属性。
可以利用 IOConnectSetCFProperty()
函数设置某个特定的属性,也可以调用 IOConnetSetCFProperties()
函数一次性设置所有属性,这在驱动程序的层面来看分别是要调用 setProperty()
方法和 setProperties()
方法
上面代码是将 myProp
设备的属性谁知为 一个 int的数字对象。
驱动程序必须要重写 setProperty()
方法不然不能成功
也可能因为 不知道有这个名称的属性或则他期望的是不同类型的对象类型,从而失败
利用 IOConnectSetCFProperties()
也是如此。
通过外部陷阱和方法进行攻击
外部陷阱是 mach陷阱系统的一部分,外部方法更像纯IOKit功能。
用户空间的代码可以通过带有6个参数的 mach 陷阱 iokit_user_client_trap() 调用索引在 IOKit驱动程序中定义的外部陷阱
内核级的用户客户端实现可以通过重写 IOUserClient
的 getExternalTrapForIndex()
方法通过这些 trap
存在两种安全问题
这里调用的数字索引在穷驱动程序内是受信任的,而且用作查找表中的所有。如果查找操作使用了未经检查的索引,攻击者可以对索引加以调整,使其从攻击者定义的内存页查找陷阱的函数指针,这有可能让攻击者立即在内核中执行代码。
可能是提供外部陷阱自身具有安全问题。
外部方法调用方式 利用 IOConnectCallMethod()
输入输出各有两种参数类型:标量和结构体。
内核漏洞攻击
有4种常见漏洞攻击在 iOS 4.3 之后,sysctl 数据项变成了只读的。
因此针对版本相对较新的 iOS 的内核漏洞攻击程序全部必须实现为百分之百ROP,除非它们居然动态代码签名能力的进程中启动的。
任意内存的重写
恶意函数
其中 copyout()
函数 吧结果返回用户空间。copyout()
函数会检查目的地址确实是否在用户空间的内存中,以确保不会吧结果写入内核内存
危险函数 ovbcopy()
这个函数不会执行任何检查,这样一来就可以把目标地址指定为内核内存中的任意位置
还有就是可以吧 getrlimit()
系统调用的结果写入内核内存。
getrlimit(RLIMIT_CORE, 0x80101010);
可以吧 rlimit
结构写入内核内存的任意位置。
rlim_t
数据为无符号整数,最高位应该是0,因此,能够任意选择的只有结果的前7个字节。
不允许 rlim_cur
的值大于 rlim_max
的值如果需要写入值
选择目标
这个攻击 大家必须知道系统调用表和内核内存中闲置空间的确切位置。内核级不存在 ASLR保护。对于相同设备和内核版本是静态的
定位系统调用表
可以利用 kdebug_enable
这样的符号轻松定位系统掉通用表,而定位系统调用表的新方法则依赖于表中的第一项结构体,以及它对于 nsysent
变量的位置
其中被设置的字段只有 sy_return_type
和 sy_call
返回类型被初始化为1,而且处理程序是某个指向内核代码段的指针。
大家可以利用 nsysent
变量来确定。
&nsysent = &sysent + sizeof(sysent)*nsysent
构建漏洞攻击程序
检查内核扩展的 Mach-O
头部后的 __PRELINK_TEXT
数据段有没有空闲空间
0x8032B300到0x8032C000
未初始化的内核变量
内核结构中为初始化的指针元素被装入来自用户空间的数据。
内核栈缓存区溢出
因为向基于栈缓存区执行了未受限制的反复操作。
返回地址重写或替换为我们的 shellcode 代码的指针。
iOS 5 中执行这种攻击会利用到一些内核级的面向返回的程序设计。
内核堆缓冲区溢出
基于堆缓冲区进行了不加限制的复制操作,这种溢出的结果取决于实际的堆实现以及附件的内存块,它们决定了这样的溢出能否用于漏洞攻击、允许任意的代码执行或者受控制的内存损坏。
内核堆内存域分配程序
zone allocator
并且通过 zalloc(), zalloc_canblock(), zfree()
函数来实现的
最常用的是 _MALLOC()
函数,这个函数会调用 kalloc
进行实际分配
较小对亏用 zalloc()
分配 较大的堆块用 knem_alloc()
函数分配
其中 _MALLOC()
是 kalloc()
外的一层外包,其中 kalloc()
包含了 两个不同的内核堆分配程序,kalloc()
是在 /osfmk/kern/kern_alloc.c
中的
关键代码
内存域 zone 结构体
所有内存域都被保存在一个 单向链表中, next_zone
指针链接。
内存域中会记录当前已经分配元素的数量和当前已分配内存的量,不会记录内存域的各内存页的地址。
其中 free_elements
指针表明,内存域中所有的自由元素都是保存在一个链表中的。指向自由列表下一个元素的连续指针存储在自由块的开头位置。
内核堆风水
ndrv_setspec()
漏洞
这个函数是链接 ndrv 套接字时要调用的处理程序。我们可以通过提供不同长度的套接字名称分配不同大小的 内核内存。
在链接的套接字上调用 close()
可以执行用户空间释放这些内存ndrv_do_disconnect()
函数实现
(1)分配足够多的内存块把这些“坑”都填上。所需的确切分配次数通常是未知的。
(2)分配更多的内存块,使这些内存块在内存中相互邻接。
(3)释放两个邻接的内存块。释放顺序取决于自由列表的实现方式。下一次分配应该返回内 存中的第一个内存块。
(4)触发有漏洞的内核函数,分配两个内存块中的第一个,并将其溢出到接着的自由块中。
(5)触发某些内核功能,分配已被重写的自由块并让已被重写的指针指向自由列表的表头。
(6)触发更多功能,分配内存,从而利用攻击者提供的指针,而不是使用真正的内存块。
(7)利用这一重写任意内存的机会,重写某些函数指针,比如系统调用表中某个未使用的处 理程序。
(8)触发被重写的系统调用,在内核空间中执行任意代码。
对内核堆缓冲区溢出漏洞的攻击
实际的堆缓冲区溢出是反复调用 ndrv_to_ifnet_demux()
函数直到一出实际的缓冲区并通过触发某个内部错误条件退出循环而引发的。
主要实现 ifnet_demux_desc
结构体中的 data指针溢出到自由列表中的下一个内存块,当下一个freechunk成为 自由列表的表头,然后控制前4个字节的内容,这4个字节指向freelist中的下一个chunk的指针,从而实现控制freelist 的表头。让他为系统调用表中的某个地址
我们需要有 两次
分配后控制freelist 表头的步骤。
越狱
不能执行未签名的代码
iOS不允许进程执行其他进程或派生进程。此外,沙盒会阻止研究者篡改 其他应用程序的文件,而且给 Mobilesafaris附加调试器也是不可能的。
越狱的类型
越狱的持久性
不完美越狱
会因为设备重启消失的越狱。
如果没有额外的内核漏洞攻击程序,就不可能禁用所有安全功能。
不完美越狱可能是由一个针对特权代码的漏洞攻击程序构成的,或是由一个针对非特权代码的漏洞攻击程序加上一个提升权限的漏洞攻击构成的。
完美越狱
利用了持久漏洞的越狱。
通常由某些不完美越狱结合可以在设备上持久存在的其他楼栋攻击程序行成。
漏洞攻击程序的类型
有的可以提供底层的硬件访问
有的只能给予沙盒内的有限权限
bootrom 级
硬件中
因为不能通过补丁修复,所以是最强大的。
能让大家替换或者修改整个引导链的每一个部分,包括内核的引导参数.
因为发生在引导链非常靠前的期间,所以漏洞攻击有效载荷可以得到硬件的安全访问权限。
例如:
可以得到 硬件加速器中的 GID密钥 从而实现解密 IMG3
文件。
iBoot 级
iBoot没烧录到硬件中,可以通过软件升级来修复
除此之外,在引导链中仍处于足够早期的位置,此时我们可以为内核提供引导参数、
为内核打补丁,或直接把硬件用于执行GD密钥的AES操作。
用户空间级
我们需要两个漏洞攻击程序才能为设备越狱。
第一个漏洞攻击程序要让设备能执行任意代码,第二个漏洞攻击程序要以一种禁用内核安全限制的方法提升权限。
以前的iOS 只要被攻击的程序以 root 用户权限运行,就可以从用户空间禁用代码签名
现在 想要禁用代码签名,必须中断内核内存或者执行内核代码。
越狱的理解
https://www.zhihu.com/question/19713924
https://www.jianshu.com/p/c5c22f9a06e2
首先 iphone 进入 DFU 模式 (设置固件升级模式)
利用 XX程序 引导iboot,然后想内存注入某个类minilinux,将他加载到 iPhone 内存中,划分一段 ramdisk ,引导这个 ramdisk的 /ect/fstab
将原来的系统盘加载进去,取得系统读写权限,改变整个目录的读写权限和属性。然后讲自己添加的命令和功能写进系统盘中。断电后,这个 ramdisk 就不在了。
ramdisk启动时,内核会运行 ramdisk的/sbin/launchd的二进制文件,它包括一个初始化越狱的小型stub程序。这个程序首先在系统里装载root根文件系统和数据分区,为修改需要,以上装载好的文件系统和数据分区是可读写。
应用转存
App Store 安装是,它们直接安装到 /var/mobile/Applications
目录中,因此可以在 iPhone 上安装多少应用取决于数据部分有多少空闲空间通常以 GB 计算。
Gydia
安装的越狱应用,Cydia
本身和所有内置二进制文件,都是安装在根文件系统的 /Applications
目录中。从而使用了 有限的应用安装空间。
解决这个问题,使用了一种名为 应用转存
的机制。
在 iPhone 的数据部分创建名为 /var/stash
的目录,并将通通常位于根文件系统中的若干目录放到该目录中。然后吗,原始目录就会被通向新位置的符号链接替代。
当前被转存到 /var/stash
目录中的目录
/Applications
/Library/Ringtons
/Library/Wallaper
/usr/include
/usr/lib/pam
/usr/libexec
/usr/share
执行内核有效载荷和布丁
权限提升
iPhone 上应用都是 mobile , _wireless , _mdsnresponder 或 _securityd
权限运行
内核漏洞攻击会把进程的权限提升为 root。
内核内部提升运行的进程的权限,修改附加进程 proc_t
结构体上的凭证 /bsd/sys/proc_internal.h
中被定义为 struct proc
想要得到 proc_t
结构体的地址 可以利用内核函数 current_proc()
内核的导出符号。
或则 通过 sysctl
接口泄露内核地址信息。 sysctl()
系统调用取回 proc_t
结构体内核地址
获得 proc_t
机构体后,我们要使用该结构体的 p_ucred
成员修改附加的 ured
结构体。
proc_ucred()
函数也可以访问这个结构体
p_ucred
字段在该结构体中偏移为 0x84
提升权限我们可以吧便宜 0x0c 位置的 cr_uid
设置为0
从而实现权限提升