从 DFG JIT 一个字节的类型错误到 iPhone 任意内存读写
本文记录了 WebKit JavaScriptCore DFG 编译器中一个类型声明错误的完整利用过程——从 DFGNodeType.h 宏表中的 NodeResultInt32(应为 NodeResultJS),经 GC 写屏障绕过触发 Use-After-Free,一步步升级到在 iPhone 真机(iOS 26.1,非越狱原厂)上稳定实现任意内存地址读写(AAR/AAW)。端到端成功率约 80%。 字段 值 漏洞位置 Source/JavaScriptCore/dfg/DFGNodeType.h — MapIterationEntryKey 节点 Bugzilla 304950 rdar 167200795 修复提交 3f6f7836068(cherry-pick 47b55468bf82) 受影响版本 Safari ≤ 26.2 (WebKit 7623.1.14.11.9) 修复版本 Safari 26.3 (20623.2.7) — 安全公告 目标设备 iPhone / vphone (iOS 26.1) 及 macOS 26.2 Exploit 成功率 ~80%(失败时通常表现为页面重载,罕见无响应崩溃) 漏洞概述 JavaScriptCore (JSC) 将 JavaScript 编译为高效机器码的过程中,有一个名为 DFG(Data Flow Graph)的中间层编译器。DFG 对每个中间表示(IR)节点附加了一个 输出类型声明(NodeResult),告诉后续优化 pass 这个节点产出什么类型的值。 问题出在一张巨大的宏定义表中,一个单词写错了: // Source/JavaScriptCore/dfg/DFGNodeType.h, line 592 macro(MapIterationEntry, NodeResultJS) \ macro(MapIterationEntryKey, NodeResultInt32) \ // ← BUG: 应为 NodeResultJS macro(MapIterationEntryValue, NodeResultJS) \ // ← 正确 MapIterationEntryKey — Map.forEach() 回调中获取 key 的节点 — 被声明为只产出 32 位整数。但看一下运行时的实际实现: ...
macOS AMFI Bypass:ObjC Runtime Swizzle 实战
背景 vphone-cli 能在 Apple Silicon Mac 上启动一个真正的 iOS 26 虚拟机。它不是 Xcode Simulator(那只是把 iOS 应用编译到宿主架构上跑),而是使用了 Apple 私有的 Virtualization.framework PV=3(Platform Version 3)API——与 Apple 为 Private Cloud Compute (PCC) 安全研究搭建的 VM 基础设施完全相同。 在底层,vphone-cli 对 iOS 的整条启动链做了二进制 patch——AVPBooter、iBSS、iBEC、LLB、TXM、kernelcache——绕过签名验证,使自定义固件能在 VM 中启动。越狱变体在启动链和 CFW 安装阶段一共施加了 127 个二进制 patch,可以在客户机内获得完整的 root/SSH/Sileo/TrollStore 环境。 这个项目非常硬核。但它需要 Apple 从不向第三方授予的私有 entitlement: com.apple.private.virtualization com.apple.private.virtualization.security-research 因为该 binary 是用这些私有 entitlement 做 ad-hoc 签名的,amfid 必然会拒绝它。此前的做法需要两步来绕过: csrutil disable 完全关闭 SIP nvram boot-args="amfi_get_out_of_my_way=1" 完全禁用 AMFI 这等于同时拆掉了 macOS 的两道核心安全防线。实际带来的问题: 开发工具链崩坏:JDK 和 Azure Functions Core Tools 在 SIP 关闭后无法正常工作。这两个工具对系统完整性保护有运行时依赖,关闭 SIP 直接导致日常开发流程断裂。 安全性急剧下降:完全关闭 SIP + AMFI 意味着任何进程都可以修改系统文件、注入系统 daemon、绕过文件系统保护、运行任意未签名代码。这在生产开发机上是不可接受的风险。 于是尝试了 amfidont,它通过 debugserver 断点拦截 amfid 的验证逻辑。但 amfidont 基于 Python 实现,实际使用中发现它不够稳定——运行需要签名验证的 binary 时经常出现长时间无响应,断点通信的延迟导致 amfid 积压请求、软件卡死。 ...
CVE-2026-20660:CFNetwork NSGZipDecoder 路径穿越导致任意文件写入
漏洞概述 CVE-2026-20660 是 Apple CFNetwork 框架中的一个路径处理漏洞,允许远程攻击者通过提供恶意 gzip 文件将文件写入受害者文件系统的任意位置。 字段 详情 组件 CFNetwork (macOS) 影响 远程用户可能能够写入任意文件 修复描述 通过改进逻辑解决了路径处理问题 修复版本 Safari 26.3 / macOS Sequoia 26.3 (2026-02-11) 发现者 Amy 漏洞类型 通过 Gzip FNAME 头的路径穿越 (RFC 1952) 触发条件 Safari “下载后打开安全文件” 已启用(macOS 默认开启) 公告 Apple Security Release - Safari 26.3 披露说明: 这是在 Apple 发布 Safari 26.3 修复后独立进行的 1-day 分析。原始漏洞由 Amy 发现并报告。所有测试均在受影响版本(Safari 26.2 / macOS 26.2.1)上对本地控制的系统进行。 阶段 1:二进制获取与 BinDiff 从 IPSW 提取 CFNetwork 使用 ipsw 工具从 iOS 26.2.1(存在漏洞)和 iOS 26.3(已修复)IPSW 固件镜像中内嵌的 dyld_shared_cache 提取 CFNetwork 框架。 ...
在 macOS 上为 IDA Pro 配置 Python 虚拟环境的简明指南
对于逆向工程师来说,保持一个干净、有序的脚本环境至关重要。为 IDA Pro 项目使用 Python 虚拟环境 (venv) 是管理依赖、避免冲突的最佳方式。 网上很多指南已经过时,推荐的方法已经不再适用。这篇文章提供一个现代、直接的方案——使用 IDA Pro 官方内置的 IDAPythonrc 启动脚本,在 macOS 上将 Python venv 连接到 IDA。 目标:自动加载 Venv 我们的目标很简单: 创建一个隔离的虚拟环境 (venv)。 使用 pip 在 venv 中安装任何 Python 包。 让 IDA Pro 每次启动时自动找到并使用这些包,无需手动操作。 现代方案:IDAPythonrc 脚本 过时的 activate_this.py 方法已经不可用——现代虚拟环境中不再包含这个脚本。正确的方法是直接告诉 IDA 的 Python 解释器你的包在哪里,通过修改 sys.path 实现。我们可以在 IDA 的用户目录中放置一个启动脚本来自动完成这个工作。 第 1 步:创建虚拟环境 首先,创建一个 venv 来存放你的工具。一个方便的位置是 IDA 的用户目录。 # 进入 IDA 用户目录 cd ~/.idapro # 创建名为 venv 的虚拟环境 python3 -m venv venv # 激活它来安装包 source venv/bin/activate # 安装你需要的包(例如 keystone-engine) pip install keystone-engine # 完成后退出 deactivate 第 2 步:创建 IDAPythonrc 启动脚本 ...
如何用 Frida 动态获取 ObjC Block 的参数类型
你是否想过窥探 iPhone 或 iPad 上的应用,观察它到底在处理什么数据?今天介绍一种简单而强大的方法——使用 Frida 动态获取 iOS 应用中 Block 的参数类型。 什么是 Block? 在 iOS 编程中,Block 是一小段可以被传递、稍后执行的自包含代码。可以把它想象成你把电话号码(Block)给朋友,朋友在有重要事情时给你回电话。 有时你想精确观察这些 Block 接收了什么输入、产出了什么结果。Frida 可以在不修改应用原始代码的情况下帮你做到这一点。 Block 的内存结构 理解 Block 的内部结构有助于你准确地获取它的签名和参数信息。以下是 Block 的内存布局: struct Block_literal_1 { void *isa; // 指向 &_NSConcreteStackBlock 或 &_NSConcreteGlobalBlock int flags; // 标志位,指示 block 类型和属性 int reserved; // 保留字段 void (*invoke)(struct Block_literal_1 *, ...); // 指向 block 实现的函数指针 struct Block_descriptor_1 { unsigned long reserved; // 保留,通常为 NULL unsigned long size; // Block_literal_1 结构体的大小 // 可选的辅助函数 void (*copy_helper)(void *dst, void *src); // flag (1<<25) 置位时存在 void (*dispose_helper)(void *src); // flag (1<<25) 置位时存在 // 包含签名的 block 必须有此字段 const char *signature; // flag (1<<30) 置位时存在 } *descriptor; // 捕获的变量(如果有) }; Block 内部的 signature 提供了返回类型和参数类型等关键信息,是动态分析的基础。 ...