Homebrew ARM9Loader -- Technical Details and Discussion

Selver

13,5,1,14,9,14,7,12,5,19,19
OP
Member
Joined
Dec 22, 2015
Messages
219
Trophies
0
XP
396
Country
This thread is for an in-depth technical review of the ARM9Loader hack.

These first posts will be edited to reflect updated information from community members. Credit will be given for corrections in the changelog, unless they were given to me via PM under condition of anonymity.

Table of Contents:

Introduction, BootRom, ARM9Loader Theory
ARM9Loader v1
ARM9Loader v2 (1/3 and 2/3)
ARM9Loader v2 (3/3), Conclusions
OTP dumping progress post / summary
Plailect's OTP Dumping Guide (external link)
Arm9LoaderHax source code (GitHub link)
Summary of differences from prior *hax (1/3)
Summary of differences from prior *hax (2/3)
Summary of differences from prior *hax (3/3)


Introduction
The description is initially based on the presentation by plutoo at 32C3, on December 27, 2015, in combination with information from the 3DS HomeBrew site, http://3dbrew.org/wiki/3DS_System_Flaws, some of my own ideas, and lots of my own ignorance thrown in. (In other words, errors are likely!)

The end goal is for a deep understanding of how and why the security broke down. Interestingly, if Nintendo had fixed some well-documented low-impact security issues (e.g., not clearing RAM on reboot, March 2014; etc.), then ARM9Loader might not have been practically exploitable.

BOOTROM
The jobs of the BOOTROM include to set some super-secret encryption keys, load firmware into memory, validate it's cryptographic signature, and BRANCH (JMP) to the firmware's entry point.

The 3DS includes both a primary firmware (FIRM0) and a second firmware (FIRM1). The second firmware (FIRM1) is used when the first firmware (FIRM0) fails the cryptographic signature check for any reason. This makes sense, as it provides a fail-safe for situations that cannot be entirely eliminated (e.g., power-loss during update FIRM0, NAND sector corruption, etc.).

Conceptually, the BootRom does the following:
  1. Lots of top-secret stuff first
  2. Load FIRM0 firmware from NAND (firmware partition) into memory
  3. Check the cryptographic signature of the loaded FIRM0 in memory
  4. If the cryptographic signature is verified, jump to FIRM0's entrypoint
  5. Else....
  6. Load FIRM1 firmware from NAND into memory
  7. Check the cryptographic signature of the loaded FIRM1 in memory
  8. If the cryptographic signature is verified, jump to FIRM1's entrypoint
  9. Else... PANIC (no towel)

Minor BootROM flaw
If the cryptographic signature of a firmware fails validation, the bootrom does not clear the memory that was written to when loading that (invalid) firmware. This allows FIRM0 to be used to load attacker-controlled contents ... up to the size of the largest Nintendo-signed firmware (because header must be signed before loading that size firmware).

So, if FIRM1 is smaller in size than FIRM0, then the attacker can still control the contents of at least some memory during FIRM1's loading.

ARM9Loader Theory
a.k.a. How it is supposed to work

ARM9Loader is an additional layer of security which was added only to the N3DS. The ARM9Loader is part of the firmware binary, and thus will only execute if the firmware itself was properly signed. The purposes of the ARM9Loader include using additional console-unique data to:
  • further decrypt the KERNEL9 binary (using OTP & sector 150)
  • perform additional key initialization prior to handing control to Process9
The general theory of ARM9Loader operation:
  1. Calculate OTP_HASH
    • SHA256 hash of the OTP areas
    • As you know, the OTP registers are console-unique data
    • OTP appears to be *THE* foundational secret for ARM9Loader
    • OTP_HASH was not cleared in some early firmware...
  2. Read encrypted sector 150 (0x96)
    • Decrypt the sector using the OTP_HASH
    • Use a 16-byte blob as Normal-key for keyslot 0x11
  3. Generate dependent keys using that keyslot
    • Dependent keys are generated using a cryptographically secure one-way function
    • Thus, the exposure of any dependent keys would not expose the parent key
    • For example, first 16 bytes used for keyslots 0x15 and 0x18
  4. Verify the keyslot (e.g., keyslot 0x11) by encrypting a fixed test-vector
    • This prevents accidental use of a corrupted NAND sector
  5. Clear the keyslot (e.g., keyslot 0x11)
    • This prevents any later use of the keyslot, such as by ARM9, to regenerate the dependent keys
  6. Decrypt the ARM9 binary
  7. Jump to the ARM9 entrypoint
 
Last edited by Selver,

Selver

13,5,1,14,9,14,7,12,5,19,19
OP
Member
Joined
Dec 22, 2015
Messages
219
Trophies
0
XP
396
Country
Arm9Loader v1


There are three versions of Arm9Loader (as of firmware 11.0.0-33), named v1, v1.1, and v2. Each is described in turn...

ARM9Loader v1 exploit

Arm9Loader version 1 is found on N3DS firmware versions < 9.5.9-X.

Version 1 uses a portion of sector 150 as an encrypted key (Key #1). This is only mentioned as contrast with version 2, because neither Kernel9 nor ARM9Loader v1 clear keyslot 0x11.

Because the ARM9Loader and Kernel9 fail to clear keyslot 0x11, all of keyslot 0x11's dependent keys can be regenerated as soon as ARM9 execution is attained. As you can deduce, once a keyslot is in use by software to store data, the keyslot's initialization vectors (e.g., KeyX, KeyY) cannot easily be changed without risk of breaking software that relied upon that keyslot.

Result
Attackers gained access to keys that depend on keyslot 0x11, for firmware 9.5.0-X and lower.

Limitations:
Neither the OTP nor the SHA256 hash of the OTP was exposed. Therefore, a v.Next firmware could simply generate a different initialization value for a (currently unused) keyslot, and depend on that new value. This is exactly what occurred, in fact, for 9.6.0-X, which used another keys from the special NAND sector, still decrypted by the hash of the OTP. At first glance, one might think that sector 150 would provide the basis for many keys.


ARMLoader v1.1
v1.1 comes only with N3DS firmware version 9.5.9-X. The only fix is that it clears keyslot 0x11.
 
Last edited by Selver, , Reason: First line to match contents, for featured posts summary

Selver

13,5,1,14,9,14,7,12,5,19,19
OP
Member
Joined
Dec 22, 2015
Messages
219
Trophies
0
XP
396
Country

Arm9Loader v2 (1/3)


Version 2 is found on N3DS firmware versions 9.6.0-X through at least 11.0.0-X (most current at time of posting).

As noted above, the bootrom won't branch to the firmware entry point unless the firmware was properly cryptographically signed by Nintendo. Arm9Loader is part of the firmware, but the remainder of the Arm9 firmware is encrypted. Arm9Loader reads an additional key from the NAND sector 150 to decrypt the encrypted Arm9 firmware.

With version 2, Arm9Loader has a bug where it does not perform the validation of the resulting decrypted key against a known test vector. As a result, if sector 150 of the NAND contains invalid data, even with 100% valid signed FIRM partition contents, Arm9Loader will decrypting the firmware into garbage due to the incorrect key and (even though the decrypted data is cryptographically random) still branch to the entrypoint address.

Presumption: The FIRM1/FIRM0 loading is part of the bootrom, and thus cannot be updated without a hardware update.

This results in what is technically called "Cryptographically Random Data" being executed, instead of the actual firmware. Now, by itself, this wouldn't seem to be useful, because the probability that the decrypted instructions would result in an exploit are 1/(an astronomically large number).

However, the 3DS has two tiny weaknesses, which greatly magnify the above bug.
#1. The BOOTROM does not clear the memory used by FIRM0 when it fails to be validated
#2. The RAM is not cleared between reboots (known for over a year)

How does #1 help an attacker?
Consider having FIRM0 containing valid header data, but otherwise filled with NOP sleds and a payload at the end of what will be loaded. The attacker can select the precise instructions in the payload, as they are loaded directly by the BOOTROM. Of course, this is normally not useful, as the digital signature will then be invalid, and thus the firmware won't execute.

Now add in a totally valid FIRM1, but one which is smaller in size than FIRM0. FIRM1 will also load into memory, but all the instructions from FIRM0 that were beyond FIRM1's valid size will remain in memory (they were loaded as part of the failed digital signature validation for FIRM0). Again, this also would normally not be useful, because FIRM1 (being a digitally signed file) will contain official firmware....

However, because Arm9Loader version 2 fails to verify the key (from sector 150) that it uses to decrypt the firmware is valid, that allows the key to be modified by an attacker. The key from sector 150 is encrypted using the SHA256 hash of the console-unique OTP. The attacker will be unable to predict the decrypted key, because the attacker does not know either the OTP nor the SHA256 hash of the OTP. Even so, the attacker can force a random 0x11 key (and relying subkeys) to be generated. This will result in the wrapped firmware being decrypted to cryptographically random data, and then (!) ARM9Loader will BRANCH right into the middle of that cryptographically random data.

Ok, but that still doesn't seem really useful, right? I mean, branching into cryptographically random instructions is useless, right? Especially when it requires a unique brute-forced key to be determined for each console, right?

... to be continued ...

ARM9Loader v2 -- 2/3

[Note that this section was written very early January 2015 ... before downgrading to obtain the OTP was possible ... and thus considers if brute-force per-console attacks were practical]

As noted above, the size of FIRM0 >> the size of FIRM1, which caused the BOOTROM to load attacker-selected instructions into memory. That's the target space for a randomly-decrypted instruction stream to BRANCH into. Alternatively, with a HARDMOD, the attacker could modify the special sector of NAND at or near the time that they write all memory, presumptively with attack code at the end of memory, plus either NOP Sleds or BRANCH sleds to increase the chance a random BRANCH instruction will get to the attack code. Because memory is not initialized to zero on reboot, this memory remains as set by the attacker. (On failure, attacker could modify the special NAND sector and reboot, or try all steps again.)

The probability of an exception (invalid memory dereference, invalid instruction) for any randomly-generated instruction is relatively close to 1/8. Thus, each boot will execute (on average) eight random instructions before throwing an exception. (Sum from N=1 to infinity of 7/(8**N) == 8, per Wolfram Alpha).

The probability of one of those eight random instructions being a BRANCH instruction is about one in eight (bits 27-25 == 101b), of which ~1/2 will have the right CONDition bits set, presuming randomness. Thus, a rough estimate is a 50% chance that a BRANCH instruction will execute for any random decryption key.

Will that BRANCH instruction point to valid memory? Surprisingly, the answer is, quite likely yes. The branch instruction is +/- 32MB (24 bits). Can an attacker force FIRM0 to be at least 32MB greater in size than FIRM1? Presumably yes, as the digital signature need not be valid for FIRM0. This is actually a limiting factor, as the header must have a valid digital signature, which limits available FIRM sizes.

So, there is approximately a 50% chance (per boot with random decryption key) of a valid BRANCH instruction being executed. Some portion will jump to another location that's filled with cryptographically random instructions (within FIRM1's size). How big is the firmware image in memory? 50% * (1 - (sizeof firmware)/256MB) == chance of hitting memory left over from prior boot, and running the exploit code.

Update: Per @173210, the ARM9 partition in 9.5 NATIVE_FIRM is 566784 bytes. This results in:
50% * (1 - (566784 / 1048576)) = .5 * (1 - .5405) = .5 * .46 = 23 % chance.

Wow... this is actually much more serious than I would have initially thought.
With a hardmod, it appears this would only take about eight writes (16 if restoring the NAND sector and resetting memory with BRANCH sled and attack code each time), before a value is found that will reliably jump to exploit code. Once found, that value can reliably be re-used on that N3DS.

Update #2: Actually, what is important is not the size of FIRM0, but how much larger it is than FIRM1. This is because most of FIRM0 is overwritten by FIRM1, leaving a much more narrow target for the JMP. However, that JMP can be to any location, because the very next instruction left over from FIRM0 can, itself, be a JMP instruction. Rather academic at this point, but...

(Continued)
 
Last edited by Selver, , Reason: added update #2 to correct overly optimistic estimates

Selver

13,5,1,14,9,14,7,12,5,19,19
OP
Member
Joined
Dec 22, 2015
Messages
219
Trophies
0
XP
396
Country

ARM9Loader v2 exploit (3/3)

OK, so based on the above, an attacker could do the following:

  1. Hardmod of N3DS that's running ARM9Loader v2
  2. Backup NAND using hardmod
  3. Boot, use exploit such MemChunkHax2 to...
    • Put attack code at the end of memory
    • Fill memory addresses with BRANCH sled to the attack code
  4. Halt everything for a period of time
    • Prevents console sending commands to eMMC
    • During the halt period, external commands overwrite the special NAND sector to result in a corrupt KEY#2
  5. After the halt period of time, reboot (leaving memory contents intact)
  6. Allow exploit time to work, wait for the BRANCH into attack code...
  7. If it fails, restore original value of special NAND sector
    • Could presume memory not substantially modified by prior run, and just modify the special nand sector again
  8. Lather, rinse, repeat....

But why bother, especially when other hacks already exist?

  • MemChunkHax2 + Downgrade to ARM9Loader v1 + firmlaunch-hax ==> generating all <= v9.5.0-X firmware keyX values
  • ARM9Loader v2 + arm9loaderhax ==> pre-ARM9Kernel ARM9 code execution, dumping of RSA keyslots, calculating 6.x save, calculating the 7.x NCCH keys, (per @AHP_person , ARM9Loader locks the OTP registers before jumping to the garbage decrypted firmware, so no OTP access :sad: ), ...
  • Decrypted OTP registers ==> ??? Because SHA256 hash of the OTP is used to decrypt the special NAND sector, could knowing the OTP data allow one to create a desired value for the decrypted key ???
See https://www.3dbrew.org/wiki/OTP_Registers

OTP register access has been disabled early on by Kernel9 (or now ARM9Loader) since firmware 3.0.0-X.
 
Last edited by Selver,

AHP_person

Well-Known Member
Member
Joined
Nov 2, 2014
Messages
364
Trophies
0
XP
498
Country
United States
  • ARM9Loader v2 + arm9loaderhax ==> pre-ARM9Kernel ARM9 code execution, dumping of RSA keyslots, calculating 6.x save, calculating the 7.x NCCH keys, reading of OTP registers, ...
This is fairly accurate, although, the OTP registers are unfortunately locked before the jump to the arm9 binary.

There's not much else to complain about in your rundown. It's nice to see people putting this info out there, so nice work! :D
 

peteruk

Well-Known Member
Member
Joined
Jun 26, 2015
Messages
2,731
Trophies
1
XP
5,660
Country
United Kingdom
I am forever the optimist and am hoping by the sharing of these kind of informations will perhaps one day lead to either an emuNAND above 9.5 for N3DS users or even a direct CFW through sysNAND

I can live in hope and these type of threads can only help
 

Ekaitz

Redhead Believer
Member
Joined
Jun 13, 2010
Messages
608
Trophies
0
XP
441
Country
France
Even it is released, hardmode seems mandatory.
Anyway, that would be great at least to get new keys.
 

Selver

13,5,1,14,9,14,7,12,5,19,19
OP
Member
Joined
Dec 22, 2015
Messages
219
Trophies
0
XP
396
Country
This is fairly accurate, although, the OTP registers are unfortunately locked before the jump to the arm9 binary.

There's not much else to complain about in your rundown. It's nice to see people putting this info out there, so nice work! :D
Thank you for this correction, AHP_Person. I've updated with attribution.
 
  • Like
Reactions: AHP_person

guitarheroknight

1.6180339887
Member
Joined
Nov 9, 2014
Messages
2,817
Trophies
0
Age
30
Location
Grand Line
XP
3,980
Country
Norway
You seem very certain, i hope your right, it's been a long time coming :)
I am because I saw it, I mean at least I think I did. There was a YouTube video showing off the capabilities of ntrhax from the SALT team or whatever they wish to call themselves and they had emunand on the latest version on a New 3DS. Now if a amateur group like them could figure out how to achieve it Im sure that the veterans like GW will surely able to do exactly the same, maybe even a community member who knows but its comming all right ;) Thats what she said.
 
  • Like
Reactions: peteruk

Suiginou

(null)
Member
Joined
Jun 26, 2012
Messages
565
Trophies
0
Location
pc + 8
XP
735
Country
Gambia, The
This would allow for emuNAND 9.6+, as long as it's launched from sysNAND 9.6+. And then if Nintendo change to a different keyslot with an update, you'll be roadblocked again.
Wouldn't having a short arm9loaderhax payload that simply loads other NATIVE_FIRMs into memory ands runs their boot process solve the problem?
 
  • Like
Reactions: peteruk

peteruk

Well-Known Member
Member
Joined
Jun 26, 2015
Messages
2,731
Trophies
1
XP
5,660
Country
United Kingdom
I am because I saw it, I mean at least I think I did. There was a YouTube video showing off the capabilities of ntrhax from the SALT team or whatever they wish to call themselves and they had emunand on the latest version on a New 3DS. Now if a amateur group like them could figure out how to achieve it Im sure that the veterans like GW will surely able to do exactly the same, maybe even a community member who knows but its comming all right ;) Thats what she said.

Ah yes, i saw that video too :)

This would allow for emuNAND 9.6+, as long as it's launched from sysNAND 9.6+. And then if Nintendo change to a different keyslot with an update, you'll be roadblocked again.

Yep this is the big problem, not sure i woould really want to upgrade my sysnand to 9.6 either, unless i had a hard mod, we need someone reliable in the UK doing N3DS mods i think
 

Syphurith

Beginner
Member
Joined
Mar 8, 2013
Messages
641
Trophies
0
Location
Xi'an, Shaanxi Province
XP
343
Country
Switzerland
Thanks for posting this. Quite a lot of details related. @AlbertoSONIC @motezazer @173210 @Steveice10 @TuxSH @d0k3
Sorry for bothering all of you. However I have some obstacles about it to share.
1,This deserves a HARD-MOD. Since only with Hard-mod can you overwrite the raw NAND sections, doing the modification. - At least currently
2.Decrytion attempts, and yes OTP. I don't know how they dumped the OTP for N3DS. Also the exploit <=2.2 is for O3DS. You must control what it produces!
3.Raw NAND access. Your code would be loaded before FIRM (if you firmlaunch it) so no fread9/fwrite9/... Even dumping something needs different APIs then.
4.Proper keys. You know you need it decrypted correctly to do a firmlaunch, or yes, an older decrypted already firm.
And, the actual result for an enhanced arm9loader hax is CFW on 9.3+ SysNAND. Not the EmuNAND directly, orz.

--------------------- MERGED ---------------------------

Well i have something to append.
1.FIRM section is 0xEF000 size, and is in .firm format, so not a bootrom, sorry for that. For my O3DS no size difference between FIRM0 and FIRM1.
And the max ever possible space for it is 4MB.
2.You can not run code on NAND due to its complexity of altering data on it. So those must be loaded to memory first.
3.However the slides may have a side-effect on it. There is another exploit, that after HARD-RESET, memory isn't cleared.
So you might set it up before the calling, temporarily. I doubt if that is from ntrcardhax.
Well but on the video it is cold booted. So you can leave this alone now.. Orz.
 
Last edited by Syphurith,
  • Like
Reactions: clank and peteruk

173210

Well-Known Member
Member
Joined
Jan 22, 2014
Messages
245
Trophies
0
Age
23
Location
Japan
Website
173210.github.io
XP
653
Country
The probability of an exception (invalid memory dereference, invalid instruction) for any randomly-generated instruction is relatively close to 1/8. Thus, each boot will execute (on average) eight random instructions before throwing an exception. (Sum from N=1 to infinity of 7/(8**N) == 8, per Wolfram Alpha).
The real exception vector is located in bootrom and to jump the another vector at the top of ARM9 RAM.
If arm9loader doesn't initialize the vector in RAM, we can use it to execute code.

So, there is approximately a 50% chance (per boot with random decryption key) of a valid BRANCH instruction being executed. Some portion will jump to another location that's filled with cryptographically random instructions (within FIRM1's size). How big is the firmware image in memory? 50% * (1 - (sizeof firmware)/256MB) == chance of hitting memory left over from prior boot, and running the exploit code.
The loaded FIRM will be copied in ARM9 internal memory, so you should assume the space is 1MB instead of 256MB.
The size of the ARM9 partition in 9.5 NATIVE_FIRM is 566784 bytes (553.5KB).
ITCM is close to ARM9 memory, but I don't take it into account because it's used by bootrom.
So, 50 * (1 - 566784 / 1048576) = 23 %
Hmm, not so bad.

If you take exception into account, the calculation will be more complex. I won't do that because I'm not good at math. :P

1,This deserves a HARD-MOD. Since only with Hard-mod can you overwrite the raw NAND sections, doing the modification. - At least currently
It's easy if you have ARM9 access. In rxTools, call tmio_writesectors(TMIO_DEV_NAND, ....). But hardmod is necessary for multiple attacks.

3.Raw NAND access. Your code would be loaded before FIRM (if you firmlaunch it) so no fread9/fwrite9/... Even dumping something needs different APIs then.
2.You can not run code on NAND due to its complexity of altering data on it. So those must be loaded to memory first.
Reading/writing NAND is quite a easy because NAND is initialized to read FIRM, but you don't have filesystem.

4.Proper keys. You know you need it decrypted correctly to do a firmlaunch, or yes, an older decrypted already firm.
And, the actual result for an enhanced arm9loader hax is CFW on 9.3+ SysNAND. Not the EmuNAND directly, orz.
You can load anything from NAND.

--------------------- MERGED ---------------------------

Wait, if you can control the exception vector, any code except calling itself or resetting ARM9 should be OK. The probability is very high.
 
General chit-chat
Help Users
    Julie_Pilgrim @ Julie_Pilgrim: some senior at my school called me a little kid, bro there's only a three year age difference