Tuesday, October 29, 2024

Looking into the Nintendo Alarmo

While everyone was waiting on news for the successor of the Nintendo Switch, Nintendo released the Alarmo. A small plastic alarm clock that can wake you up with sounds from your favorite Nintendo games. While I was hesitant to buy one at first, I eventually decided to get one and look deeper into how it works.

Just an Alarm Clock?

The Alarmo has a small 2.8-inch LCD at the front, a back and notification button on top, and a dial on top which can be turned and pressed to act as a confirm button. The dial also includes an RGB LED. What makes it different from other alarm clocks? It has 2.4 GHz Wi-Fi to download software updates and additional themes, and it comes with a 24GHz mmWave presence sensor to react to your movements. With a hefty price of 99€, I didn't want to buy one at first. But after a few days I decided it could be a fun project to look into how it works behind the red plastic shell, so I bought one.

 

What's inside?

Since I placed my order right before the weekend, I had to wait until a week after launch before the Alarmo finally arrived. In the meantime, someone on Twitter named Spinda already found some SWD debugging pins on the board, so the first thing I did was open up the Alarmo.
Getting to the board is really straight forward. There's a single phillips screw at the bottom of the device, next to the USB-C port. After removing the screw the screen simply twists off the front together with the board attached to it. On the board, there's an STM32H730ZBI6 MCU and a KIOXIA 4GB eMMC.
After soldering wires to the SWD pins and connecting to the Alarmo with my Pi, I could use OpenOCD to peek and poke at the Alarmo's memory and registers. Unfortunately I couldn't read out the STM's internal flash where the system starts running the firmware from, due to a mechanism called readout protection (RDP). This mechanism prevents any access to the internal flash once a debugger has been detected. To defeat RDP we need to find a way to achieve code execution without attaching a debugger.
What's awesome about the STM32H7 is that the reference manual and lots of example code and libraries are freely available. The debug port also allows loading a payload into memory and executing it, so there's already lots of potential things which can be done here.

 

Is This Flash?

Since Spinda was posting some massive progress on Twitter, I decided to contact her. She told me to take a look at the 0x70000000 range in memory. Dumping from there indeed reveals a lot of ARM instructions. This area seems to contain most of the firmware!
According to the reference manual this is the so called OCTOSPI2 range. OCTOSPI is a low-level interface which is used for single/dual/quad/octal SPI communication. SPI? Is there an SPI flash on the board that I missed? There seems to be a small chip without any useful markings next to the MCU. Could that be it? After starting to reverse engineer the firmware, which I just dumped, it seems to treat the OCTOSPI range as RAM. And indeed, writing a value to an unused part of this area and resetting the system causes the value to reset back to 0x55. After examining the OCTOSPI register configuration, this seems to be 32 MiB of HYPERRAM used for external RAM. Unfortunately this doesn't help us to defeat RDP, since we can't have a persisting payload in external RAM.

 

Picture of what seems to be the external RAM on the board.
 

Decrypting the Contents

So where is this firmware in RAM coming from? It's getting loaded from the eMMC on startup. Spinda had already dumped the eMMC at this point, using the eMMC functions provided by the firmware in RAM. The eMMC contains a content folder with files for each of the game themes, a system file, a factory file and a file called 2ndloader.bin. Unfortunately all of the content files are encrypted. But how does the system decrypt them? The STM32H7 contains a cryptographic processor called CRYP. This peripheral can be configured and then used to decrypt content in memory. The firmware dumped from external RAM doesn't configure CRYP, so it's most likely getting configured by the code on the internal flash, which runs on startup. But the firmware is using the CRYP interface to decrypt the contents, and so can we! The CRYP interface is configured for AES-128-CTR, which makes things easier. Since, in CTR mode, a keystream is created, which is then combined with the plaintext to encrypt and decrypt files, we can simply create a large amount of this keystream using the CRYP interface, and then combine it with the encrypted files to decrypt them. After dumping around 100MB of keystream using the debug interface, we were able to decrypt all files on the flash.

Bonus: Obtaining the Key

When configuring the CRYP interface, the key is placed into four 32-bit registers. Unfortunately reading out the key from those registers isn't possible, since they are write-only. Brute-forcing also isn't a viable option since there are 2128 different possible combinations.
While Spinda was already looking into the contents of the eMMC (She found lots of interesting stuff, keep an eye on her Twitter!), I started talking with hexkyz about the findings. Hexkyz noticed that the CRYP interface is vulnerable to a partial overwrite attack. And indeed, since the key is split up into 4 different registers it's possible to only update 32 bits of the key and then try out all 232 different possibilities until matching output is produced by the crypto processor. This needs to be done for all four parts of the key, so we need to test for a total of 4×232 different combinations, which is possible to do in a few hours. After writing a small payload to perform this, I let it run overnight. The next morning I checked the progress and it was done, I had successfully obtained the AES-128-CTR key used to encrypt and decrypt the Alarmo content files.


sha256(alarmo_content_key)=47238c47d21165fdb2f9a26c128e4b620a39139f6514588f5edb8a16397a9201

 
The initialization vector can simply be read out from the IV registers, as these aren't write-only.

Update 2024-10-31:

It's also possible to encrypt known plaintext blocks on the Alarmo and then progressively overwrite parts of the key with zeros, while dumping the resulting ciphertext for each part. Then the brute force attack can be performed on a PC, by incrementing the key until the output matches the ciphertext obtained with only parts of the key not set to zero. For this I've created a PC tool using the AES-NI x86 extensions. Doing this on a PC reduces the time from several hours to a few minutes (on a somewhat modern PC). I've uploaded the tools for this to the GitHub repository. Thanks to @SciresM and @PoroCYon for pointing this out!

File Format Overview

Now that we are able to decrypt the files, let's take a look at them. All content files on the eMMC are encrypted and prefixed with a CIPH file signature magic. Let's call them CIPH- or cipher-files throughout this post. The bodies of those CIPH files are AES-128-CTR encrypted. The last 256 bytes of the encrypted body make up a RSA-2048 signature (PKCS#1 v1.5 with SHA256).
All themes, as well as the system and factory files, are .shpac files. These files are simply ZIP files wrapped inside of a CIPH file.
Every shpac archive contains all sorts of assets and a firmware binary. These firmware binaries start with a BINF file signature magic, so let's call them BINF-files. Every BINF file has a header, which contains the address where the file should be loaded to in memory, the address of the vector table, and the total size of the file in memory.

The 2ndloader

The most interesting part of the contents on the eMMC is the 2ndloader. As the name implies, this is a secondary loader that's loaded from the eMMC. The 2ndloader is the only content file which isn't a shpac file; Instead, it's loaded and decrypted by the loader on the internal flash, directly into SRAM (@0x24000000). During a normal boot, the 2ndloader will load a firmware from the encrypted system.shpac file on the eMMC, copy it into the external RAM, and then jump to it. Surprisingly the signature of the system.shpac is not checked during a normal boot, only when performing a firmware update.

 

Firmware Updates

Firmware updates are relatively straightforward. The system firmware queries an endpoint, using a device unique certificate, for the latest firmware version, which then responds with a CDN link to an updated system.shpac. This file then gets downloaded and stored as system.update.shpac on the eMMC and the device reboots into the 2ndloader. The 2ndloader verifies the signature of the updated file and then copies it over the existing system.shpac. During this copy process a small progress bar is drawn on the screen. Then the signature of the new system.shpac is checked.

 

USB Loader

The most interesting part of the 2ndloader is the USB mode. When all three buttons on top of the Alarmo are held during boot, the 2ndloader sets up a USB mass storage device with a FAT32 formatted buffer in external RAM. It then waits for a MarkFile to be placed on the device. Once that file is found, it reads a CIPH file from the device, which contains a BINF firmware. Like all other CIPH files, this file is encrypted and signed. After decrypting the file and loading it into external RAM, the 2ndloader jumps to the newly loaded reset vector, like it does for the regular system firmware.
They are checking the signature for the USB payload, right? Right? Well you could say they tried... Their code looks something like this:
if (!IsSignatureValid("2:/a.bin")) {
// Do nothing
}

// Continue with loading the firmware


This allows us to load arbitrary firmware binaries over USB without even opening up the Alarmo. The file still needs to be encrypted, but that's easy now that we have the key or even just a large enough keystream.

Picture of a custom payload running on the Alarmo, displaying a picture of a cat.

 

Defeating RDP

Now that we can run arbitrary code without needing a debugger attached, it's possible to avoid triggering the flash readout protection. So I ended up writing a small payload which attempts to copy the contents of the internal flash to RAM. Unfortunately this didn't work and I was only reading zeroes. While the read protection error was no longer being set, there was another error in the way: The secure error flag was set.
Turns out the STM32H7 has another protection mechanism in the way called "Secure Access mode".

Secure Access Mode

In Secure Access mode the MCU always boots into a secure bootloader made by STM. This bootloader supports setting up a secure area containing secure user code, which can then perform various tasks. After the secure user code finishes, it jumps to the regular application and gets locked out. While we don't know what the secure user code exactly does in case of the Alarmo, it has to be responsible for setting up the CRYP interface and is going to load, decrypt, and jump to the 2ndloader. Right before jumping to the 2ndloader the secure user area gets locked out and is no longer readable. I unfortunately haven't been able to dump either the secure bootloader nor the secure user area. So what exactly happens in there remains a mystery for the time being.

Bonus: Exploiting the 2ndloader

While reverse engineering the 2ndloader, I noticed that it uses RSS_exitSecureArea to jump to the loaded firmware. That's the function which is used to lock out and leave the secure user area. So the 2ndloader might still run in secure mode? So I started looking for a way to gain code execution in the 2ndloader before it jumps to the loaded code and exits the secure area. Remember the BINF file which gets loaded in USB mode? It has an address in the header where it's supposed to get loaded to. Spinda asked, "Will it copy to any address you specify in the bin file header?". That's a good question! I remember seeing code which verifies that it needs to be within external RAM. After double checking, only the vector table address needs to be within external RAM. The file itself can be loaded anywhere. This allows overwriting instructions from the 2ndloader, causing a jump to custom code before RSS_exitSecureArea gets called. Unfortunately reading out the internal flash still triggered the secure error. So it looks like the 2ndloader doesn't actually still run in secure mode, even though it's using the function to exit from the secure area.

 

What's next?

So what's next? There's now a way to run custom code on the Alarmo without opening it up. This currently still works on software version 2.0.0, and there doesn't seem to be a system in place for updating the 2ndloader yet. Of course, it's technically possible to update the 2ndloader on the eMMC, so we'll see what's going to happen.
What about the secure user area? I'm currently out of ideas on how to dump it for now. Since we already have the key, there probably aren't many other interesting things we could gain from dumping it, besides of course getting confirmation on how it works.

When this blog post goes public, I'm releasing my testing USB payload which performs display initialization and shows a picture of a cat. I'm also going to release the payload I used to obtain the content key. You can find both of those in the GitHub repository here: https://github.com/GaryOderNichts/alarmo
Maybe more people are going to look into writing custom code for the Alarmo.

Credits

  • Spinda for figuring out the SWD pins, writing code to dump the eMMC, and listening to my 2ndloader ramblings.
  • hexkyz for helping with finding resources about the secure area and giving me the idea to dump the key.


Saturday, October 28, 2023

Exploiting DNS response parsing on the Wii U

It's annual Wii U exploit time! 😄

Image of the Wii U connection test screen on the GamePad.

After reverse engineering parts of the Wii Us' NET stack for another project I was working on, I realized it's using a modified version of NicheStack.
NicheStack is a TCP/IP stack developed by InterNiche Technologies and is designed for use in embedded systems.

INFRA:HALT

If you end up searching for NicheStack on the internet, one of the first things you'll find is a security research report called INFRA:HALT, published by Forescout Research Labs and
JFrog Security Research. This report contains a set of 14 vulnerabilities, which all affect various parts of NicheStack.

While most of these vulnerabilities "only" lead to a DoS, two vulnerabilities might lead to remote code execution. One of them affects the HTTP server, which is not used on the Wii U. The other one sounds a lot more interesting though...

CVE-2020-25928

CVE-2020-25928 is a vulnerability in the DNS client, which is also in use on the Wii U. It has a CVSS v3.1 Score of 9.8 and results in a heap buffer overflow which can lead to remote code execution.

The code described by the INFRA:HALT write-up looks something like this:

uint8_t *cp;
cp = (uint8_t *)&dnshdr[1];
dns_entry->alist[0] = 0;
dns_entry->ipaddrs = 0;

for (i = 0; i < records; i++)
{
// ...
cp = getoffset(cp, (char *)dns, &offset);
cp = getshort(cp, &type);
cp = getshort(cp, &netclass);
// ...
cp = getlong(cp, &ttl);
cp = getshort(cp, &rdlength);
switch (type)
{
// ...
case 0xCu:
if ( type == 1 && rdlength != 4 )
err = 7;
if ( !err )
{
++dnsc_good;
if ( i < ( queries + answers ) )
{
if ( nameoffset == 0 )
{
nameoffset = offset;
// ...
}
dnc_set_answer(dns_entry, type, cp, rdlength);
// ...
}
else
{
if ( nameoffset == offset )
{
dnc_set_answer(dns_entry, type, cp, rdlength);
}
// ...
}
// ...
}
break;
// ...
}
}

As we can see dnc_set_answer is called for each record with type 0xC (PTR). 

The dnc_set_answer implementation does something like this:

void 
dnc_set_answer(dns_querys *entry, unshort type, uint8_t *cp, int rdlen)
{
// ...
switch ( type )
{
// ...
case 0x0c:
memcpy(entry->ptr_name, cp + 1, rdlen - 1);
// ...
break;
// ...
}
}

As we can see the DNS client basically copies the record data into a fixed size buffer on the heap without checking the size.

At this point I was interested and decided to take a look if the Wii U implementation suffers from the same issue. To my surprise the Wii U implementation looks something like this instead:

if (type == 0xC)
{
dnc_copyin(dns_entry->ptr_name, cp, dns);
}
else
{
dnc_set_answer(dns_entry, type, cp, rdlength);
}

The affected dnc_set_answer call is explicitly not called for the type 0xC (PTR) records. Instead a function called dnc_copyin is called, which does proper bounds checks.

So someone (at Nintendo?) fixed this several years before INFRA:HALT was discovered and disclosed? Well, they tried!

As we can see from the full code above there are two places where dnc_set_answer is called. The first one for answer records and the second one for additional records pointing to the first answer. But they only added the check for the first one?!

The second one still blindly calls dnc_set_answer without doing any size checks.

Exploiting it on the Wii U

After writing a quick PoC server to confirm this actually works on a Wii U, I decided to try and exploit this.

Since the dns_querys struct is stored on the heap, this is a basic heap overflow. The Wii U added a few additional fields to the dns_querys struct, which we can overwrite. Due to the NET stack running on the ARM co-processor, the Wii U later copies this entire struct back to the PPC side using an address stored in a reply struct. Unfortunately to overwrite the pointer to this reply struct, we would need to spoof a reply struct and point to it. Since the heap layout is determined by various network setup specific factors we have no idea where in the heap this dns_querys struct is allocated. So we need a way to store a buffer at a location in memory which we know the address of.

Introducing DNS over TCP

This doesn't seem to be part of NicheStack, but the Wii U features support for DNS over TCP instead of UDP. After two attempts of using UDP, the Wii U will switch to TCP if the truncated flag was set in the DNS header. For TCP support more fields were added to the dns_querys struct, one of them is a pointer to a buffer in which the received TCP data is stored.

So just overwrite this with an address in the stack and we can receive data into the stack? Unfortunately, no. Before storing data into this buffer it is resized using IOS_HeapRealloc (basically realloc), if it isn't NULL. If the heap block is already the same size as the reallocated size, the same pointer is returned though. This allows us to take over already allocated blocks inside of the heap but limits us to stay in bounds of the heap. If we point the receive buffer to one of the already allocated packet buffers at the start of the heap, which will always have the same address, we can receive data into a known memory location (limited by the MTU and TCP/IP fragmentation).

Remember the reply struct which is pointed to by the dns_querys struct? We now have a known memory location at which we can create a fake reply struct which points the reply buffer to anywhere in IOS-NET memory. This causes the entire dns_querys struct to be copied to anywhere we want and we can control approximately 256 bytes in this struct!

Memory layout showing dns_querys copying.

Creating a ROP Chain

Since the Wii U features no-execute on the ARM side, we need to write a ROP chain which performs a kernel exploit. Doing this within 256 bytes is tricky, but it's enough to open up a TCP socket, connecting to a server and receiving data from it into memory. We're abusing some buffering of the TCP stack implementation here, but it works!

We can now perform a stack pivot into the next ROP chain. 

I wish more IOS modules had a gadget to load the stack pointer from the stack!

In this ROP chain we perform a kernel exploit as described in the previous write-ups, and copy the kernel binary from the buffer we received over the socket in the previous ROP chain.

We now have kernel code execution!

Presenting: DNSpresso

Since everything on the Wii U needs to be coffee related this implementation is called DNSpresso :p

You can find the GitHub repository with usage instructions here.

Monday, January 23, 2023

Looking into the Stadia Controller Bluetooth Mode Website


With the end of Google's Stadia platform on January 18, 2023, Google published a website allowing people to "Switch the Stadia Controller to Bluetooth mode".
This seems pretty cool, but there are two points listed under "Important things to know" which I didn't like:
  • Switching is permanent
    Once you switch your controller to Bluetooth mode, you can’t change it back to use Wi-Fi on Stadia. You can still play wired with USB in Bluetooth mode.
  • Available until December 31, 2023
    You can switch to Bluetooth mode, check the controller mode, and check for Bluetooth updates until Dec 31, 2023.
While permanent switching is not a huge issue, since Stadia isn't available anymore, and the Bluetooth mode is way more useful, I still wanted to have the option to switch back.
Since the Stadia Controller's WiFi approach is rather unique, I didn't want to just disable it and no longer have the option to look into it.
 
But only one year to update the firmware and then you're stuck in "Wi-Fi mode" forever? I guess Google really wants to forget about Stadia forever, and get rid of the site after a year.
 
So I started looking into the switching process on the site, to try and avoid those limitations. I also reverse engineered some parts of the binaries hosted on the site, more about that later.

Analyzing the Bluetooth mode website

The JavaScript used by the site is minified which won't give us function and variable names. It doesn't stop us from seeing what it does and analyzing the packets using Wireshark though.
Note that most of the flashing process seems to be standard NXP stuff, and only contains some minor adjustments by Google. 

The site uses WebUSB and WebHID to communicate with the controller. It filters for several different Vendor and Product ID combinations, to determine the state/mode the controller is currently in.

The switcher loads several files from the data endpoint, which we'll take a look at in more detail later. From taking a rough look at the files and the logs in the JS, the "Bluetooth mode switcher" actually flashes a firmware update to the controller. So from now on I'll be referring to this as "flashing the Bluetooth firmware" and the site as "flashing tool/site".
 
 The site starts by checking the firmware revision and battery percentage while the controller is in the normal, powered on mode, this is referred to as "OEM Mode".
 

OEM Mode

While in OEM mode, after plugging in the controller to the PC without holding down any buttons, the site communicates with the controller using WebUSB.

It starts by checking the first two bytes of the serial number from the USB string descriptor. There are some prefixes which are not allowed to be flashed. The serial prefix is also used to determine if this controller is a development controller (dvt) or a production controller (pvt).

It then retrieves the current firmware revisions using USB control request 0x81.
Firmware revisions less than 0x4E3E0 are referred to as gotham, while all later revisions are called bruce. gotham being the old Wi-Fi firmware, while bruce is the new Bluetooth firmware.

After that the battery percentage is requested using control request 0x83 and retrieved with request 0x84. This value is used to check if the controller has enough charge (more than 10%) to perform the flashing process.

After all that info has been retrieved, the site asks us to unplug the controller and turn it off.
 

Bootloader

The site now wants the user to hold down the Options button, while plugging the controller back in. This will enter the Bootloader.
Not much to say about this mode. The site asks us to press Options + Assistant + A + Y while in the Bootloader, which will enter the SDP Mode.
 

SDP Mode

SDP (Serial Download Protocol) Mode allows sending several low-level commands to the controller.
The flasher uses WebHID to send and receive commands.
It starts by uploading a signed Flashloader binary (restricted_ivt_flashloader.bin) into the controller's memory (@0x20000000), by using the SDP WRITE_FILE command.
It then jumps to the uploaded Flashloader binary (@0x20000400) using a JUMP_ADDRESS command.
The controller is now running the Flashloader.

 Flashloader

The Flashloader is a bit more advanced than the previous modes. It can also receive and send several commands via USB, and the flasher site once again uses WebHID to send and receive those commands.
Google seems to have chosen a restricted version of this Flashloader though, since only a few commands actually used by the flasher are available.
Also only a few, small memory regions are allowed to be read and written using the WriteMemory and ReadMemory commands.

The Flashloader is used to actually write the new firmware into the controllers flash storage.
 

Detecting the MCU Type

The site starts by detecting the MCU type, by reading from 0x400D8260. There are two supported types (106XA0 and 106XA1), if the detected type doesn't match one of them it will throw an error.
 

Detecting the Flash Type

Since different Stadia Controller models seem to have different flash storage types, the exact chip is now detected. Detecting the flash type is a bit of an interesting approach. 
To communicate with the flash storage a FlexSPI configuration block needs to be loaded and applied. To determine the flash type, the site retrieves the device ID from the flash. It starts by uploading a special configuration block for determining this ID (flashloader_fcb_get_vendor_id.bin) into memory (@0x00002000), and applies this configuration using the ConfigureMemory command.
This configuration block contains some sane values for the different flash chips, and also contains a lookup table (LUT) with different FlexSPI sequences which will be sent to the flash chip.
For the get_vendor_id configuration the first sequence in the LUT, usually used for reading from flash, has been replaced with a Read Manufacture ID/ Device ID command.
Now comes the interesting part: The site now directly configures the FlexSPI registers using ReadMemory/WriteMemory Flashloader commands via USB.
It configures the FlexSPI FIFO and sends the Read Device ID command from the LUT sequence.
It then retrieves the result from the first RX FIFO Data Register.
It seems like writing to and reading from those few FlexSPI registers is explicitly allowed in the flashloader.

Setting up the Flash Storage

Now that the flash type is known the site can load the proper configuration block for that chip.
There are two supported flash types (Giga-16m and Winbond-16m).
To setup the Winbond chip an entire flash configuration block (flashloader_fcb_w25q128jw.bin) is loaded and applied.
For the Giga the flash is automatically configured by the Flashloader based on a simple configuration value (0xC0000206).
 

Flashing the Firmware

Now that everything is ready the actual firmware flashing can begin.
After clearing GPR Flags 4-6, the site loads the signed target firmware image (<bruce/gotham>_<dvt/pvt>_a_<dev/stage/prod>_signed.bin) and parses some build info values from it.
It also determines where in the flash the firmware should be flashed to. To flash data the site sends a FlashEraseRegion command to erase and unlock the flash, followed by a WriteMemory command to write to the flash mapped in memory @0x60040000.
The IVT (Image Vector Table) is now flashed to @0x60001000 (only if the image contains one), and the actual firmware application gets flashed to the proper slot location (Application A / Application B).

Cleaning up

Now that the firmware is flashed, GPR6 is set to the proper application slot and a Reset command is issued to restart the controller.
And that's basically it, the controller is now running the newly flashed firmware.

Dumping the old Firmware

As mentioned in the beginning, it is not possible to revert to the old Wi-Fi firmware using the Stadia mode switching site, once the new Bluetooth firmware has been flashed.
While the site does seem to technically support flashing the old Wi-Fi firmware, and also has references to the firmware files required for it, all those files lead to a 404 and can't be downloaded.
So to preserve the old Firmware I had to dump it from the controller itself.
 
I tried to read from the flash memory region while in the Flashloader, which only results in errors. It seems like reading from flash is not allowed by the restricted Flashloader.
 
But I had another idea...
Remember that we have direct access to some of the FlexSPI registers, which are used to determine the flash type?
 
Instead of applying the get_vendor_id configuration block and sending the Read Device ID command, I tried applying the proper flash configuration and sending a Read Data command over the registers.
That surprisingly did work without any issues. I could now issue FlexSPI read commands via USB and dump the flash.
 
Since only reading the first register of the RX FIFO Data Registers is allowed by the restricted Flashloader, I had to dump the flash 4-bytes at a time, which did take several hours.
At the end I had a full dump of the Stadia controller flash though!

Finishing up

During the testing I started reimplementing parts of the site in Python which I called stadiatool, which also allowed me to mess around with the Flashloader commands.
After dumping the flash, I extended the tool to allow flashing the firmware as well.
Note that this was a pretty quick project which is why the code might seem rushed.
You can find the GitHub repo here.

That's it for now, I might take a look at analyzing the firmwares themselves next.

Special thanks to cmplx for some help while analyzing this and for listening to my random ideas!

Looking into the Nintendo Alarmo

While everyone was waiting on news for the successor of the Nintendo Switch, Nintendo released the Alarmo. A small plastic alarm clock that ...