Stage 4 : getting access to privileged syscalls by taking over ro, a sysmodule, from spider/SKATER (aka rohax)
Description : the 3DS runs a system module named “ro” (for relocatable object probably ?) which allows users to load and run dynamically linked code (“CROs”). These CROs are most commonly stored directly in romfs and are checked with a central CRR file which is signed and contains hashes for each CRO file in the application. The CRR’s signature is checked once when that file is loaded, and then each CRO’s integrity is checked by comparing it against the corresponding hash stored in the CRR file. CRO files contain a list of patches which must be applied to the code being loaded so that it can be relocated. The ro system module has access to a number of high-privilege system calls which, among other things, allow it to give regions of memory executable status within its own virtual memory space as well as other processes’.
Problem : using GPU DMA, we are able to modify the loaded CRR file’s hash list *after* the signature check has occured. This, in turn, lets us load arbitrary unsigned CRO files. It is then possible for us to manufacture a custom CRO which will use the relocation patch list to patch ro’s stack, give us ROP capabilities within ro and use that to get ro code execution using SVC 0×70. This is not supposed to be possible as relocation patches can only be within CRO segments, and CRO segments are checked to be within CRO memory when first parsed, which is great. However, what we can do is extend one of the CRO segments to include the CRO segment table (which is within the CRO itself, not copied elsewhere for actual use), then use our first relocation patch to extend a CRO segment to include the stack, and then use subsequent CRO patches to write our ROP chain to the stack.
Limitations : getting code execution in ro is great because it gives us access to svcControlProcessMemory, which lets us map memory as executable within other processes. It also allows us to modify other processes’ memory using svcMapProcessMemory. That being said, we can’t do that with just any other process (like, ideally, loader…) as we need a handle for that other process, and ro does not have access to svcOpenProcess. Therefore, we only use our code execution capabilities within ro to remap memory within Cubic Ninja.
Fix : this was fixed in 9.3 by adding a bunch of (apparently) proper bound checks to ro’s various commands. I haven’t looked into it myself but I’ve been assured it’s pretty much airtight.
Summary
- ninjhax => Cubic Ninja ROP
- Cubic Ninja ROP => gpuhax => Cubic Ninja code execution
- Cubic Ninja code execution => gpuhax => spiderto => spider ROP
- spider ROP => rohax => ro code execution (ro becomes “hb” custom service handler)
- ro code execution => spider code execution => Cubic Ninja code execution with memory remapping and basic cache management capabilities, as well as access to sdmc and a bunch of new services