文章总结: 文档详细分析了2026年6月公开的AppleA12/A13BootROM级别漏洞usbliter8,该漏洞源于DWC2控制器硬件设计缺陷,通过USBSetup包长度校验漏洞实现内存覆盖。漏洞影响iPhoneXR至11等设备且无法软件修复,目前PoC支持降级模式和启动未签名iBoot,为越狱提供基础但需专用硬件工具。文章深入解析了A12/A13不同的利用链及PAC绕过技术,并给出后利用方案。 综合评分: 86 文章分类: 漏洞分析,二进制安全,移动安全,逆向分析,漏洞预警
苹果越狱新漏洞?A12/13 BootROM 级别漏洞 PoC 已公开
原创
i3eg1nner i3eg1nner
SecureNexusLab
2026年6月20日 10:45 北京
在小说阅读器读本章
去阅读
usbliter8 是 Paradigm Shift 团队于 2026 年 6 月 18 日发布的 Apple A12/A13 SecureROM(BootROM)漏洞利用,属于自 checkm8(2019)以来最重要的 BootROM 级别漏洞。
发现这个漏洞的起点是研究人员注意到一个奇怪现象:USB控制器在收到小于标准长度的Setup包时,内存写入位置出现了偏差。顺藤摸瓜,他们定位到DWC2控制器的硬件设计缺陷,环形缓冲的复位逻辑假设所有包都是标准长度,但这个假设不成立。
根据GitHub公开的PoC,目前它提供降级生产模式和启动未签名 iBoot 两项能力——这与 checkm8 为 palera1n 越狱提供的核心原语完全一致,因此理论上是 A12/A13 设备(iPhone XR 至 iPhone 11 系列等数百万台设备)越狱的基石,但由于漏洞位于只读 BootROM 中无法修补、同时需要专用硬件工具且每次重启必须重新利用,从 PoC 到完整越狱工具仍需开发自定义引导加载器、适配内核补丁等大量工程工作。GitHub链接如下:
https://github.com/prdgmshift/usbliter8
USB Setup包的那些事
每次你把iPhone插到电脑上,电脑(host)要给手机(device)发指令,都得走USB的控制传输。控制传输的第一步永远是一个Setup事务。
这个事务里有两个包,都是host发给device的。第一个是Token包,告诉设备地址和端点号。第二个是Data包,里面装着一个8字节的Device Request结构体。
就像这样,
规范规定这个Data包必须正好8字节,格式严格。USB驱动拿到的就是这个结构体,原封不动。
为了后面说清楚,我们先把这个8字节的payload叫做Setup包。
到这里一切都还很正常。问题出在硬件层面。
一个在DWC2控制器里躺了可能十年的bug
苹果SoC上用的USB控制器是Synopsys的DWC2。这个东西的驱动在Linux内核里也有,你可以去看它的实现。
我们要关注的核心是,控制器怎么把收到的数据写到内存里。
AP(应用处理器)配DMA的方式很简单,分配一块内存,然后把物理地址写进控制器MMIO区域的一个叫DOEPDMA的寄存器里。控制器收到SETUP和OUT包的数据后就往这个buffer里塞。
关键细节来了,研究团队观察到,当USB控制器写数据的时候,它会直接递增DOEPDMA寄存器里存的那个地址值。也就是说,这个寄存器不只是个配置项,它实打实地充当了下一次DMA传输的目标地址。
这就埋下了祸根。
DWC2控制器的设计是这样的,它会在内存里连续存最多三个Setup包。
当第四个Setup事务到来时,DMA基地址会被重置到起始位置再写,跟ring buffer的逻辑差不多。
每写完一个收到的包,控制器会把DOEPDMA递增已写入数据的大小。重置操作则是把DOEPDMA直接减24。
看起来很合理的环形缓冲逻辑对吧。
但问题来了,控制器也接受小于标准大小的包(虽然存储时总是按4字节对齐)。
指针递增的量跟固定减24的复位量不匹配了。
结果呢,我们拿到了一个每次12字节步长的buffer underflow原语。
研究团队认为这是USB控制器本身的硬件bug。虽然可能影响很多设备,但这个漏洞只在特定条件下才能被利用。
重点来了,他们确认A12和A13的SecureROM受影响,而A11不受影响。
A11为什么没事?因为A11的USB驱动在每收到一个包后会手动把DMA地址重置回初始值。软件层面的防御把硬件bug给堵上了。
A12和A13的情况是,USB DART被配置成了bypass模式,这意味着我们可以随意覆盖SRAM里的数据。A14及之后,SecureROM里DART被正确配置了,所以这个漏洞虽然硬件层面可能还存在,但利用路径被掐断了。
这就解释了为什么漏洞影响范围刚好卡在A12到A13这一代。
A12上拿PC控制,简单粗暴
A12上拿到PC控制简直不要太直接。
原因很巧妙,USB控制器的DMA buffer分配在堆上,而且刚好紧挨着USB任务的栈。
最简单的做法就是,用buffer underflow一路往上覆盖,把栈上保存的LR干掉。等调度器切回USB任务的时候,PC就是你的了。
没有PAC保护的年代,ROP链就是这么朴实无华。
A13上,PAC来了但故事没结束
A13的SecureROM引入了PAC(Pointer Authentication),情况一下子复杂了很多。研究团队观察到,PAC似乎只对栈上保存的LR做签名。但这已经足够防住A12上那个直接覆盖栈LR的套路了。
而且一路上还有不少防御要绕。堆元数据有校验和,在堆操作的时候会验证。上下文切换时LR要验签名,USB任务每次被唤醒处理数据包时都会触发。经过一些迭代之后,他们搞出了一个多步骤的技术来拿PC控制。我尽量把这个链路的逻辑讲清楚。
「第一步」,覆盖USB控制器DMA buffer前面紧挨着的DART相关数据。这会给他们一些很有限的写原语,在退出DFU循环的时候触发一次。
他们利用了某些清理函数里可控的数据,把全局的DART分配指针给清零了。这一步很关键,不然后面损坏的分配在释放时触发堆校验检查就会panic。
这里有一段代码,你可以感受一下,
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linevoid dart_stop(unsigned int dart_id){
dart = darts[dart_id]; mmio_base = dart->info->mmio_base; v4 = 16 * dart->ctx->field_11 + 0x200; for ( int i = 0; i < 16; i += 4 ) {
*(_DWORD *)(mmio_base + v4 + i) = 0; } dart_flush_maybe(mmio_base);}
「第二步」,同一个清理路径里,他们利用一个0xf的写原语覆盖了全局的panic计数器。
这样做的效果是,下一次panic的时候CPU不会重启,而是进入一个无限循环。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linevoid __noreturn panic(){
if ( ++panic_cnt >= 3 ) spin();
}
「第三步」是个精细活。他们必须避免破坏USB任务的上下文,特别是LR和SP寄存器,这些在上下文切换时会被保存和恢复到任务结构体里。
做法是掐准时间,让DMA写发生在USB任务处于活跃状态的时候。这样任务yield出去的时候,正确的寄存器值会覆盖掉之前被破坏的。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line Task structure 0x000 ┌─────────────────────┐ │ │ │ Task state, │ │ scheduler data, │ │ crit. section depth │ │ │ 0x030 ├─────────────────────┤ │ │ │ Other registers │ │ │ <──┐ ├──────────┬──────────┤ │ This area needs to │ LR │ SP │ │ be overwritten while ├──────────┴──────────┤ │ USB task is running │ │ <──┘ │ Safe to overwrite │ │ │ 0x1b0 └─────────────────────┘
「第四步」,现在可以安全地覆盖任务结构体里一个追踪临界区深度的字段了。让它在IRQ开着的情况下触发panic,这样执行会进入第一步设好的那个无限循环,但中断服务程序仍然可以运行。
而且此时USB控制器还保持在可以继续往内存写数据的状态。
「最终步」,所有铺垫做完之后,他们可以一路覆盖内存,直到抵达存放USB IRQ handler指针的全局变量。覆盖它,就拿到了PC控制。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line00000000 struct irq_handler_ctx // sizeof=0x1800000000 {00000000 void (*handler)(void *arg);00000008 __int64 *arg;...00000018 };
void handle_irq(){ ... handler = irq_list[irq_num__].handler; if ( handler )
handler(irq_list[irq_num__].arg); ...}
到这里,A13上PC控制的整个链路就走通了。比A12多绕了五个弯,但每一步的逻辑都咬得很紧,没有任何”巧合能过”的侥幸感。
拿到代码执行之后呢
PC控制只是开始。SecureROM运行在EL0模式,这意味着我们不能访问受保护的内存区域,比如MMU页表和ROM相关的元数据,也不能动SCTLR、TTBR这些特殊寄存器。
SecureROM可以切到EL1,通过执行SVC 0指令。这不是syscall,handler只是简单地在EL1模式下回到SVC之后的那条指令继续执行,之后很快会执行ERET回到EL0。
ROM里做这个切换的地方很少。研究团队选了负责把执行权交给下一阶段iBoot的函数里的一个位置。
A12和S4/S5的后利用
没有PAC的SoC上,就是经典的ROP。
链很短,就几帧,
- 往USB MMIO做一次32位写入,改DMA目标地址
- 睡大概400毫秒
- 睡醒后跳到0x1000088B0
DMA目标被设到了boot trampoline内部的某个位置。这块内存在EL0下当然不能写(甚至EL1也不行,它是只执行区域),但DMA嘛,不受这个限制。
boot trampoline总是在同一个地址。
400毫秒的延时给了exploit足够的时间把shellcode写进boot trampoline。
延时结束,执行跳转。固件里其实有检查,防止你跳到函数中间执行。它维护了一系列状态标志,每步更新。如果前一步的标志没设,固件就panic。
有意思的事情发生了。编译器为这些检查生成了下面这段代码,
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line...1000088C8: ADRP X9, #0x19C014033@PAGE1000088CC: LDRB W10, [X9,#0x19C014033@PAGEOFF]1000088D0: CBNZ W10, loc_10000891C1000088D4: STRB W8, [X9,#0x19C014033@PAGEOFF]1000088D8: LDRB W8, [X24,#0x19C014032@PAGEOFF] # 危险的检查... 走的X24!1000088DC: CBZ W8, loc_10000891C...
X24是callee-saved寄存器,它的值存在栈上。就是那个我们完全控制的栈。
所以这一步直接绕过了。
拿到EL1权限后要做的清理工作不少,
- 把原始的boot trampoline拷回原位
- 恢复所有被破坏的堆分配,还好数量不多而且都是静态数据
- 注入自己的USB控制请求handler,放在boot trampoline区域里未用的空间,覆盖BSS里对应的回调指针
- 往USB序列号字符串里注入PWND
- 仔细修复usb_task()的栈帧,把执行还回去
DFU模式现在带着我们的自定义handler在跑了。
A13的后利用,PAC让事情更有趣了
A13不能直接ROP。但巧的是boot trampoline区域刚好夹在BSS和堆之间,所以在exploit过程中他们可以覆盖它。
跳转函数有个和A12类似的问题,步骤验证用的地址存在栈上(X22),但在A13上不太好直接控制。
他们找到了一个很漂亮的gadget,
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line# A13 SecureROM...10000A5E8: MOV X22, X0 # 跳到这里,X22立刻可控10000A5EC: ADRP X10, #__stack_cookie@PAGE10000A5F0: NOP10000A5F4: LDR X10, [X10,#__stack_cookie@PAGEOFF]10000A5F8: STUR X10, [X29,#var_38]10000A5FC: LDR X10, [X22]10000A600: ADD X10, X10, #0xF10000A604: AND X10, X10, #0xFFFFFFFFFFFFFFF010000A608: MOV X11, SP10000A60C: SUB X23, X11, X1010000A610: MOV SP, X2310000A614: LDR X10, [X22,#0x10] # 来自可控位置的函数指针!10000A618: MOV X1, X2310000A61C: MOV X2, X910000A620: MOV X3, X810000A624: BLRAAZ X10 # 跳到jump func,认证是假的...
研究团队提到,固件大量使用带认证的分支指令,但实际上只有IB key被启用了。这个细节对整个利用来说简直是天赐良机。
A12上清理已经够头疼了,A13因为一路覆盖破坏得更加彻底。他们的解决方案很巧妙,重启SecureROM,让它自己把所有东西重新初始化。
唯一的问题是,我们的修改要怎么在重启后存活下来。
做法是把ROM拷到SRAM的最末尾,然后想怎么patch就怎么patch。
但ROM期望从原始地址0x100000000执行,不是从SRAM。它经常做PC相对寻址访问SRAM(0x19C000000),从意外位置跑的话这些访问就会打到空中去。
绕过的办法是配置MMU,让SRAM物理地址上拷贝的代码映射到原始ROM虚拟地址。
MMU从SRAM里ROM代码的最开始就被启用了。过程中的某个时刻,它会创建自己的转换表然后切过去,一切就会坍塌。但可以hook ROM的PTE生成逻辑,让它继续按我们的意愿路由。
补丁列表大概是这样,
- 缩小load area,让ROM不会去清除我们藏代码的最后几百KB SRAM
- 强制进DFU,不从NAND启动
- Hook USB序列号生成,加PWND标识
- 注入自定义USB请求handler
搞定。现在的SRAM和load area都足够大,偷几百KB不会造成什么问题。
总结
usbliter8这个exploit说明了一件事,即使是最新一代使用了PAC的SecureROM,一个细微的硬件bug仍然可以导向完整的代码执行和信任链的突破。
BootROM的安全是根基级别的。这个层面的漏洞会影响整个设备的完整性。当然苹果的SEP(安全隔区处理器)在攻击者和用户数据之间加了额外一道安全边界,usbliter8不直接影响SEP本身,但它确实打开了更宽的攻击面来进一步研究Secure Enclave。
公开这个exploit的意义,研究团队说得很克制,他们希望记录这类硬件漏洞的真实影响,为理解现代BootROM安全做出贡献。
BootROM漏洞的稀缺性让它每次出现都像一个仪式,提醒你在这层只读代码里,偶尔还是会有惊喜留给那些愿意花时间深挖的人。
从A11的手动重置DMA地址,到A12/A13的bypass模式DART,再到A14之后正确配置的DART,你几乎能看到苹果的安全团队是如何一代代收紧防线的。但A12和A13已经出厂了,它们的BootROM烧在硅里面,没法改,这些设备将带着这个漏洞走完整个生命周期。
在Paradigm Shift看来,推动安全进步既需要防御性工程,也需要深度的攻击性研究。像usbliter8这样的项目帮助我们理解现有防御的实际局限,提升对复杂攻击面的认知,最终让系统变得更坚韧。
每条gadget的选择、每一步绕过、每一个因为编译器行为而产生的运气,背后都是成百上千个小时的逆向和实验。这才是安全研究迷人的地方,不只是最后那个PWND的字符串,而是你亲眼看到一条链从硬件的细微bug开始,一步步被搭起来,最终撑开一扇门。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:SecureNexusLab i3eg1nner i3eg1nner《苹果越狱新漏洞?A12/13 BootROM 级别漏洞 PoC 已公开》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论