ShofEL2, a Tegra X1 and Nintendo Switch exploit
Welcome to ShofEL2 and Switch Linux, fail0verflow’s boot stack for no-modification, universal code execution and Linux on the Nintendo Switch (and potentially any Tegra X1 platform).
Choosing whether to release an exploit or not is a difficult choice. Given our experiences with past consoles, we’ve been wary of releasing vulnerability details or exploits for fear of them being used primarily for piracy rather than homebrew. That said, the¹ Tegra bootrom bug is so obvious that multiple people have independently discovered it by now; at best, a release by other homebrew teams is inevitable, while at worst, a certain piracy modchip team might make the first move. 90 days ago, we begun the responsible disclosure process with Google, as Tegra chips are often used in Android devices. The disclosure deadline has now lapsed. The bug will be made public sooner or later, likely sooner, so we might as well release now along with our Linux boot chain and kernel tree, to make it very clear that we do this for fun and homebrew, and nothing else.
… is what we were planning to say, but then someone published the 0day bug two days before our 90-day disclosure window was set to expire on April 25th. Oh well. Yes, this is the same bug that is exploited by fusée gelée, and that was just leaked by some other group (but we found it first).
This is going to be a bit rough since we didn’t have the blogpost/repos ready (procrastinators as we are), but without further ado, here’s the scoop.
What’s this all about anyway?
The Tegra X1 (also known as Tegra210) SoC inside the Nintendo Switch contains an exploitable bug that allow taking control over early execution, bypassing all signature checks. This bug is in the RCM mode, which is a USB-based rescue mode intended for initial flashing of Tegra devices and recovery of bricked devices. Normally, RCM mode only allows signed images to be loaded, but thanks to the bug, arbitrary code execution is possible.
This means that in order to gain code execution on the Switch you need to do two completely independent things:
- Enter RCM mode
- Execute the USB-based exploit
Each can be accomplished in several independent ways. Note that this is what iPhone users would call a “tethered jailbreak”, in that it needs to be performed on every boot via USB.
Since this bug is in the Boot ROM, it cannot be patched without a hardware revision, meaning all Switch units in existence today are vulnerable, forever. Nintendo can only patch Boot ROM bugs during the manufacturing process. Since the vulnerability occurs very early in the boot process, it allows extraction of all device data and secrets, including the Boot ROM itself and all cryptographic keys. It can also be used to unbrick any Tegra device as long as it has not suffered hardware damage or had irreversible changes (e.g. fuses blown). And since this is a boot-time bug that does not require touching the onboard eMMC storage, its use is completely undetectable to existing software. You can dual-boot Linux (via the USB exploit) and the Switch OS (via normal boot) with impunity, forever, as long as you do not try to make changes to the on-board memory (e.g. you can store the Linux filesystem on a second SD card partition or another SD card).
Entering RCM mode
On the Switch, RCM mode can be entered in multiple ways:
- From prior kernel-mode code execution on the system, e.g. using a WebKit exploit and kernel exploit as an entry points
- If the eMMC is removed, the Tegra will enter RCM mode on boot
- If you hold the Volume Up, Home, and Power buttons on the Switch (not joy-cons) at the same time.
Note that the Joy-Con home button won’t work here. You may be wondering about the secret home button on the Nintendo switch itself. As it turns out, what Tegra calls the Home button is actually connected to Pin 10 (the rearmost pin) on the right hand side Joy-Con connector. You can just use a simple piece of wire to bridge it to e.g. a screw on the rail (easiest), or pins 10 and 7 (or 1) together (10 and 9 won’t work). You can also 3D print a little jig to do this easily, using the innards of a Micro USB connector (which has the same pin pitch as Joy-Con connectors), or use a disemboweled Joy-Con as a donor for the connector. The latter is also useful, because the Joy-Con rails are UARTs and we use the right hand joy-con port as the console for coreboot, u-boot, and Linux.
Executing the USB-based exploit
The USB exploit requires a USB host. The exploit also requires using very long control transfers, which unfortunately some OSes are not happy with. You can either use vanilla Linux on a PC with an xHCI controller (USB 3.0, or any USB port on most recent systems), or a PC with an EHCI (USB 2.0) controller and this kernel patch.
This could conceivably also be executed from an Android phone (at least those with xHCI controllers), although porting the exploit to Android is left as an exercise to the reader. Bonus points for doing it from another Tegra device. Like another Switch.
Linux on Switch boot chain
Our boot chain is 99% open source, based on the Pixel C boot chain (because who wants to use Nvidia’s messy L4T kernel fork and proprietary bootloaders?). It looks like this:
BootROM Exploit → coreboot loader → coreboot → ARM trusted firmware → coreboot → u-boot → Linux
The only closed-source component, which is optional, is the Tegra210 DDR4 memory training code. For unknown reasons, this is released as a binary blob for the Pixel C, which gets copied into memory and jumped into. This blob can be obtained from the Pixel C factory image as follows:
./build/util/cbfstool/cbfstool bootloader-dragon-google_smaug.7900.97.0.img extract -n fallback/tegra_mtc -f tegra_mtc.bin
Make sure you get a tegra_mtc.bin with this SHA256:
Although this blob contains hardcoded tables and a “do everything” entrypoint for the Pixel C, it is possible to jump in one level deeper into the call tree and pass in custom tables, which is what our patched coreboot does to configure DDR4 on the Switch. Without this blob, the stack will still work, but the memory will run at ~200Mhz, which needless to say rather cripples performance.
The boot process looks like this:
- The Tegra starts up the Boot ROM on the BPMP (arm7) core, and enters RCM mode.
- The host-based USB exploit code puts a small (~2.5K) loader (cbfs.bin) into SRAM memory, using an RCM command.
- The exploit is triggered, causing the ROM to jump into cbfs.bin.
- The host code then goes into CBFS server mode, dynamically serving coreboot.rom via the USB RCM pipe to the Tegra.
- cbfs.bin uses this CBFS server to request the first 28KiB of coreboot, which is the coreboot bootblock, into SRAM, then jumps into it.
- The coreboot bootblock initializes basic peripherals, then loads the romstage into SRAM using the CBFS service from USB and jumps to it.
- The coreboot romstage initializes SDRAM (at the basic fixed boot speed) and other peripherals.
- The romstage then downloads the entire coreboot ROM into SDRAM (still using the Boot ROM RCM mode USB routines) and switches to RAM-backed CBFS.
- The romstage now loads the ramstage (from the RAM CBFS), starts up the CCPLEX (Cortex-A57), and shuts down the BPMP.
- Now on the CCPLEX (at EL3), the coreboot ramstage performs the rest of the basic system initialization, including DDR4 training if the MTC blob is available.
- The ramstage now jumps into ARM Trusted Firmware, which initializes the TrustZone implementation. This is required to provide certain OS services.
- ARM Trusted Firmware returns to the coreboot ramstage, now running in EL2 mode.
- The ramstage loads and jumps to its final payload, U-Boot.
- U-Boot starts, initializes some more hardware, and, by default, goes into USB download mode, with its own USB driver.
- Using imx_usb_loader on the PC side, you can load arbitrary U-Boot payloads, such as a Linux kernel and its initramfs, and run them.
Have a look at our Git repositories:
Sorry, we don’t have a guide for the Average User to use this, nor should they, most likely, since a lot of things are very rough around the edges. If you’re serious about developing, you should be able to figure things out yourself or ask questions on IRC.
The Git history is quite ugly, as we haven’t had a chance to clean things up yet. Please do not stare too hard.
Proper write-up on the exploit, stories of porting Linux (yes, there’s more to it than a panel driver ;-) ), upstreaming status, how batshit insane DDR4 is, and more. Probably, anyway. We’re pretty bad at writing blogposts.
¹ There are actually many Boot ROM bugs, several of which have been found by multiple people.