ns2jb 存储库现已公开,其中包含针对任天堂 Switch 2 的漏洞利用代码。这是一个纯软件实现的绑定式越狱工具(Tethered Jailbreak),允许用户以普通系统模块或内置文件系统(fs)系统模块的身份运行任意原生代码。

该工具使得在 Switch 2 上运行自制软件、逆向工程及修改游戏成为可能,同时支持从 Switch 备份文件以及通过 MMIO 访问大部分外围设备。据开发者透露,这是 Switch 2 平台上首次出现此类级别的漏洞利用。
为了演示越狱功能,Codex 修改了 Switch 2 独占游戏《马里奥赛车世界》(Mario Kart World)。开发过程中得到了 Dabney House 团队成员在角色选择与测试方面的协助。
实验环境搭建涉及两台主机(Switch 1 和 Switch 2)、两个 JetKVM 设备以及一个 SwitchBot。
关于自动化提示词的使用情况:虽然标题暗示了某种自动化流程,但实际上并未成功实现全自动目标导向的代码执行。越狱本身的开发耗时约两周,而《马里奥赛车世界》的修改工作仅用一天完成,主要出于娱乐目的。
JetKVM 负责通过 USB 向 Switch 发送控制器输入信号,并通过 HDMI 接收视频输出。当 Switch 2 发生崩溃时,需要物理按下电源键重启。为此,实验使用了 Amazon 的蓝牙开关机器人:当机械臂下降时触发电源键,完全按下后剩余扭矩将机械臂抬起,从而实现自动重启循环。
在控制台越狱研究中,WebKit 浏览器引擎通常是首选的攻击面。Codex 最初也尝试以此作为代码执行的切入点。
通过浏览器执行代码
该方法声称利用自定义 Wi-Fi 封闭门户加载 WebKit n-day 漏洞来实现“原生控制流劫持”。
研究目标是实现在微内核服务(Sysmodules)中执行代码。然而,通过替换 JavaScript 函数对象的方法无法突破现有限制,研究者需要一种新的方式来进行任意 IPC(进程间通信)调用,这面临巨大挑战:
- 即使拥有任意读写内存权限,也难以确定可执行任意 IPC 调用的具体设备位置。
- 即便定位到设备,由于 PAC(指针认证码)的存在,构建签名指针变得极为困难。
- 即使能构建签名指针,如何调用该指针仍是一个未知数。
鉴于上述技术障碍,Codex 调整了策略。虽然 WebKit 的可访问性和任天堂 BBP 奖励计划曾激励许多人尝试此路径,但在当前阶段,其迭代效率较低。
通过 smilehax2 执行代码
新方案转向针对 Switch 1 的游戏进行攻击,主要基于两点考量:
- 这些游戏由第三方开发商构建,通常不需要处理复杂的 PAC 和 CFI(控制流完整性)保护,尤其是 Switch 1 本身不支持 PAC。
- Switch 1 的信任根属于所有者,因此可以解密并读取代码。
尽管可以从 JavaScript 获取浏览器攻击面的优势(类似 Electron 架构下的 Discord RCE),但研究者选择了另一条路径:SmileBASIC。此前 SmileBASIC 曾在 3DS 平台的 smilehax 中被攻破,提供用户空间代码执行。作为一个庞大的 C++ 代码库,其 BASIC 解释器中存在可利用内存损坏漏洞的可能性较高。
在解释器中发现了一个关键的 32 位整数溢出漏洞。当从数组中删除项目时,解释器检查 position + count <= array.length,但该求和结果可能发生回绕变为负数。为了填补删除元素造成的空白,剩余元素会被左移:
memmove(
backing + position * 8,
backing + (position + count) * 8,
);
假设有一个长度为 4 的字符串数组 R,执行命令 REMOVE R,&H60000000,&H3FFFFFFF。position + count 回绕至 0x9fffffff。作为有符号 32 位整数,该值小于 array.length。由于乘以 8 的操作同样发生回绕,最终导致执行 memmove(backing, backing - 8, 32),从而将前一个 8 字节的数据拉入数组中。尽管命令报告了“Out of memory”错误,但数据复制已经完成。
这一漏洞极具利用价值,因为 SmileBASIC 中任何数组前面的 8 个字节都包含指向数组本身的指针。在执行 REMOVE 后,对 R[0] 的操作会将数组的支持结构视为一个字符串。由于指针占据了 R[0] 的长度字段,字符串长度变得极大,从而允许通过子字符串操作实现对内存的任意读取和写入。
由于 SmileBASIC 是 Switch 1 游戏,漏洞开发主要在 Switch 1 上进行,而 WebKit 代理则在 Switch 2 上并行工作。经过一段时间的努力,团队成功获得了 ROP(返回面向编程)链。
获得初步代码执行能力后,下一步目标是妥协更高特权的进程,特别是普通系统模块和内置系统模块。
由于缺乏源代码,难以直接判断哪个系统模块具备足够的特权。Codex 被指派实现系统模块代码分析的目标,大多数实例专注于音频和 IDN 模块,可能是因为这些模块在 Switch 2 上存在已知错误或 GPU 驱动相关的高价值目标。然而,最终突破口来自内置的文件系统模块(fs)。
fs 系统模块
选择 fs 模块的原因包括:
- Switch 1 的代码显示 fs 能够提供解密的代码。
- 该模块代码量大,攻击面广。
- 文件系统 API 变化不大,Switch 2 的 fs 模块可能与 Switch 1 相似。
首要原因是 fs 能够解密其他系统模块的代码,从而使 Codex 能够离线分析这些模块中的错误。虽然 ldr 和 ro 等模块有权映射可执行内存,但其攻击面相对较小,特别是在来自 SmileBASIC 的限制下。
缓冲区溢出与路径正常化
IFileSystem 处理器有一个名为 GetEntryType 的 IPC 命令,它接收一个路径字符串并通过 Path::Normalize 进行规范化处理。
如果路径中包含带有反斜杠的父目录组件(例如 /..\),会调用到一个分配 0x301 字节临时堆缓冲区的辅助函数。在某些情况下,规范化后的路径可能超过缓冲区大小,导致缓冲区溢出。
为了利用这一溢出,需要一个合适的受害者分配流入。Codex 发现 BufferedStorage 对象为其文件分配了缓存条目。如果该条目设置了“脏”位,读取和写入操作将跟随指针缓存数据。结合任意读写(ARW)能力,这一机制可被进一步利用。
通过扫描内存找到 SmileBASIC 的连接对象并将保存的 PID 设置为 1,研究人员得以执行有特权的文件系统操作。随后,内部存储和所有普通系统模块代码被转储,供 Codex 进行离线分析。
自制软件和修改通常依赖于执行自定义的原生代码。唯一允许映射可执行代码的普通系统模块是 ro。这些系统模块均受到 CFI、PAC 和 XOM(执行只读内存)的保护,且接口简单。相比之下,寻找逻辑错误比直接在 ro 中寻找内存损坏更为有效。
重新审视 fs 模块提供解密代码的功能,有助于转储普通系统模块的代码。理解程序加载流程至关重要:游戏和普通系统模块分发为任天堂内容档案(NCA),其中包含一个名为 ExeFS 的文件系统图像。ExeFS 内含主可执行文件和程序元数据文件 main.npdm。
加载程序时,内置的 ldr 系统模块要求 fs 打开 ExeFS,fs 通过 IPC 处理器回复文件系统信息。fs 还提供关于 NCA 头部的加密信息:头部的 SHA-256 哈希值和该哈希值的开发者签名。装载器使用这些信息执行两项签名检查:
- 验证任天堂对
main.npdm中进程权限和开发者公钥的签名是否有效。 - 验证开发者对 NCA 头部哈希值的签名是否有效。
理论上,密码学链条是完整的:NPDM 签名确定了进程权限和开发者密钥;该密钥固定了 NCA 头部的签名;签名修复了 NCA 头部的哈希值;哈希值确定了 NCA 头部的内容(包括文件系统哈希);这些哈希值最终修复了可执行内容。
然而在实践中,确保服务的文件系统匹配 NCA 头部哈希值是 fs 模块的责任。既然已经获得了对 fs 的 ARW 权限,这一信任链即可被打破。
任意对本机代码执行的 r/w
利用 ARW,可以用自定义的临时文件系统替换加载器 IPC 处理器背后的 ExeFS 对象。这个临时文件系统仍需为加载器的第一个检查提供有效的开发者 main.npdm,但可执行文件本身可以是任意的。第二次检查将通过,因为当加载者打开文件系统时,fs 已经提交了原始文件系统的有效 NCA 头部哈希和签名。
实施过程涉及两次关键写入:
- 第一次写入:将创建的临时文件系统对象置于加载器 ExeFS 的域表条目中。该临时文件系统包含原始的
main.npdm和替代的主文件。 - 第二次写入:将加载器的 ExeFS 对象放入临时文件系统的域表条目中,以保持引用计数一致并防止 FS 崩溃。
这些写入必须发生在加载器请求 ExeFS 文件系统之后,但在 fs 处理后续文件请求之前。为了确保时序,fs 进程维护其 IPC 连接列表。当连接有新消息时,fs 进行处理。利用 ARW,可以将 fs 等待的加载器 IPC 连接的句柄替换为 SmileBASIC 控制的事件句柄,从而精确控制 fs 处理加载器消息的时机。
借此,可以在拥有对应 main.npdm 的任何程序中执行任意代码。
获取内置系统模块的原生代码执行
目前拥有的 main.npdm 涵盖了所有普通系统模块和游戏。利用 ARW 和加载器的 fsp-srv 连接,可以复制任何普通系统模块或游戏的 NPDM。
该能力被用于启动自定义代码,使用 ro 的 NPDM。如前所述,ro 的 NPDM 授予其调用内核 SVC(如 SetProcessMemoryPermission、MapProcessCodeMemory 和 MapProcessMemory)的权限。在此案例中,使用 MapProcessMemory 创建 fs 现有代码的可写视图,从而可以读取并转储 fs 的代码。
这些调用需要目标进程的 KProcess 句柄。为了获取 fs 的句柄,利用 ARW 覆盖 IEventNotifier 的 4 字节句柄为 0xffff8001,然后从 SmileBASIC 调用 GetEventHandle。内核将此值解释为“当前进程的句柄”,因此 SmileBASIC 获得了 fs 的 KProcess 句柄。
随后,将该句柄发送给自定义的 ro 进程,由其修补 fs 的代码。
在任何普通系统模块权限下进行任意代码执行具有强大的潜力。这使得在不首先利用每个设备驱动程序的情况下调查硬件成为可能,有望帮助自制社区在定制固件(CFW)方面取得进展。
短期内,几个值得关注的后续步骤包括:
- 将
fs系统模块漏洞移植到 WebKit。一旦明确了要利用的错误,执行任意 IPC 调用的价值降低。或许可以通过更改 WebKit 现有文件系统代码使用的数据,在fs中触发 ARW。 - 目前的越狱是绑定式的,除了
fs之外没有其他妥协内置系统模块的方法。由于fs是内置系统模块,SM(安全监视器)跳过来自fs的服务 ACL 检查,提供了类似 smhax 的能力。未来可能会探索其他内置模块中的漏洞。 - 通过在
fs中执行原生代码,可以直接编排其加密硬件。例如,用于 NCA4 解密。 - 该漏洞已在 Switch 1 上测试,但尚未打包发布。它不提供 Modchip 的全部功能,但不需要外部硬件。
对于 Switch 2 未来的自制软件、模拟器和游戏修改,前景令人期待。





