Switch System Flaws: Difference between revisions
| (16 intermediate revisions by 3 users not shown) | |||
| Line 1: | Line 1: | ||
This page is a list of publicly known Switch flaws. | This page is a list of publicly known Switch / Switch 2 (S2) flaws. | ||
= Hardware = | = Hardware = | ||
| Line 1,087: | Line 1,087: | ||
| October 13, 2024 | | October 13, 2024 | ||
| [[User:Yellows8|yellows8]] | | [[User:Yellows8|yellows8]] | ||
| | |- | ||
| [[Bus_services|sasbus]] StartPeriodicReceiveMode infoleak | |||
| StartPeriodicReceiveMode writes a vtable ptr into the mapped tmem at +0. The tmem is mapped RW in the user-process. There is no clearing of tmem during tmem cleanup. Hence, the user-process can read the tmem to obtain a Bus-sysmodule codebin-region infoleak. This vtable-ptr seems to be unused - it's also empty after the first two entries (stubbed incref/decref). | |||
[20.0.0+] Removed the vtable ptr, with data intended for the user-process being moved from tmem+0x8 to +0x0. Also, instead of calling memset, funcs are called for manually clearing tmem. | |||
| Bus-sysmodule infoleak, which allows defeating ASLR. | |||
| [[20.0.0]] | |||
| [[20.0.0]] | |||
| February 22, 2022 | |||
| May 3, 2025 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |||
| [[NFC_services|nfc]] SendCommandByPassThrough buffer overflow | |||
| SendCommandByPassThrough eventually copies the input buffer into a fixed-size heap buffer, without size validation. | |||
This was fixed with [20.0.0+] by clamping the size. | |||
| nfc-sysmodule heap buffer overflow. | |||
| [[20.0.0]] | |||
| [[20.0.0]] | |||
| Late November 2021 | |||
| May 3, 2025 | |||
| [[User:Yellows8|yellows8]] (maybe others?) | |||
|- | |||
| [[HID_services|hidbus]] EnableJoyPollingReceiveMode infoleak | |||
| The tmem initialized by hidbus EnableJoyPollingReceiveMode contains a vtable ptr (tmem+0x10), hence infoleak. With [20.0.0+] the vtable ptr write was removed, and tmem is now memset starting at tmem+0x10 instead of +0x20. | |||
| hid-sysmodule infoleak, which allows defeating ASLR. | |||
| [[20.0.0]] | |||
| [[20.0.0]] | |||
| March 2020 | |||
| May 4, 2025 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |||
| [[SSL_services|ssl]] Certificate verification bypass | |||
| The ssl sysmodule keeps a list of trusted certificates, that are imported by an app with ImportServerPki. During certificate verification, if the certificate that is provided by the server has the same subject key id as a trusted certificate, the certificate is accepted, even if self-signed. A blog post about this vulnerability can be found [https://reversing.live/sslbypass.html here]. | |||
| Man-in-the-middle for any connection that uses ImportServerPki. | |||
| [[20.2.0]] | |||
| [[20.2.0]] | |||
| June 6, 2025 | |||
| August 8, 2025 | |||
| [https://github.com/kinnay Yannik] | |||
|- | |||
| [[LDN_services|ldn]] AdvertiseData OOB-memcpy with EncryptionType3 (AES-128-GCM) actionframes (ldnhax) | |||
| The ldn action-frame parser object for AES-128-GCM (used with [[LDN_services|EncryptionType3]]), when it does validation once finished, only verifies that the sizes are within bounds of the input buffer. There's no validation against constants, which the other EncryptionType objects have. The caller code doesn't validate the size either. | |||
[21.0.0+] Now validates the advert-size with sizeof(NetworkInfo.AdvertiseData). | |||
For more details see [https://gist.github.com/yellows8/16bb56343d085d2db2ab0adc5d4cef99 here]. | |||
| Compromise of ldn starting from OOB-memcpy, even on S2: stack infoleak (ASLR defeat), arbitrary memory read/write (which also allows handle-leak), vfunc-calls with arbitrary [[Security_Mitigations|vtable]]. | |||
| [[21.0.0]] | |||
| [[21.0.0]] | |||
| June ~13, 2025 | |||
| November 11, 2025 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |- | ||
| | | [[HID_services|hid:dbg]] AttachHdlsVirtualDevice unvalidated DeviceTypeInternal | ||
| | | hid:dbg AttachHdlsVirtualDevice eventually passes the input from HdlsDeviceInfo into a func without any validation. The DeviceTypeInternal field is used as the index for loading a ptr from a global array. The only validation occurs when the loaded ptr is NULL - this is just for initializing the ptr in the array when it's not already set. | ||
| | |||
| [[ | Since the highest DeviceTypeInternal is value 30, using >=31 will load an OOB ptr. This ptr is written to state, and also immediately passed to a called func. As long as ptr is valid it should be fine with this func. | ||
| [[ | |||
| | This functionality is also used eventually by ApplyHdlsNpadAssignmentState and ApplyHdlsStateList. | ||
| | It's unknown whether there's a way to exploit this. Also note that hid:dbg is not normally accessible to retail titles. | ||
| | |||
[21.0.0+] Arrayindex=0 is now used when the input is invalid. | |||
| Likely useless, even if reachable? | |||
| [[21.0.0]] | |||
| [[21.0.0]] | |||
| June 3, 2024 (possibly eariler(?)) | |||
| November 14, 2025 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |- | ||
| | | [[Bluetooth_Driver_services|bluetooth]] BSA allowed ATT MTU is too large | ||
| GATT-handler stack buffer overflows with a large input size are only possible if the payload_size (MTU) field in state is large enough. gatt_client_handle_server_rsp/gatt_server_handle_client_req will drop messages where the size is >= payload_size (though unless the request opcode matches certain values it will also send an error-response for invalid-PDU). Both of these handle updating this field when needed, however that's handled properly. | |||
| | |||
| [[ | With bluetooth-classic via L2CAP, a hard-coded MTU of 0x205 is sent in the configure request. However the code handling received configure requests will set payload_size to 0x2A0 if no MTU is specified, or the input MTU if it's within range 0x30..0x2A0. Hence, sending data large enough for buffer overflows requires bluetooth-classic via L2CAP + manually sending large ACL data. | ||
| [[ | |||
| | [15.0.0+] gatt_l2cif_config_ind_cback which handles the received configure-requests with bluetooth-classic mentioned above, now uses MTU range 0x30..0x205 with the default MTU being 0x205. It is therefore no longer possible to trigger the previously mentioned buffer-overflows with bluetooth-classic. | ||
| | | Stack buffer overflows in bluetooth-sysmodule due to the allowed MTU for ATT being larger than the stack data. | ||
| | | [[15.0.0]] | ||
| [[20.0.0]] | |||
| November 2021? | |||
| November 26, 2025 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |- | ||
| | | [[Bluetooth_Driver_services|bluetooth]] BSA gatt_process_prep_write_rsp stack buffer overflow | ||
| BSA gatt_process_prep_write_rsp memcpys to stack without size validation (the input len param which is subtracted to determine the copy-size is also unvalidated). Triggering this is only possible if the system sent ATT_PREPARE_WRITE_REQ, and then received ATT_PREPARE_WRITE_RSP with a large size. | |||
| [[ | The size used with memcpy is (u16)(insize-4), so when insize is less than 4 the copy size will be {negative value masked to u16}. This will therefore eventually crash when the stacktop is reached during memcpy. | ||
| [[ | |||
| | [15.0.0+] Paritially fixed due to corrected MTU handling (doesn't apply to negative-copysize). [21.0.0+] Fully fixed with proper size validation. | ||
| | | Stack buffer overflow in bluetooth-sysmodule when the required ATT messages are sent/received. | ||
| | | [[21.0.0]] | ||
| [[21.0.0]] | |||
| November 2021? | |||
| January 19, 2026 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |- | ||
| [[NFC_services|nfc]] Initialize buffer overflow | |||
| All Initialize* cmds for nn::nfc::detail::IUser (nfc:user), nn::nfc::detail::ISystem (nfc:sys), nn::nfp::detail::IUser (nfp:user), nn::nfp::detail::ISystem (nfp:sys), nn::nfp::detail::IDebug (nfp:dbg), nn::nfc::mifare::detail::IUser (nfc:mf:u): these copy the input array into _this, without validating the array count. | |||
The data is copied to obj_impl+0x8+0x28, with each entry being 0x20-bytes. The event handle returned by AttachAvailabilityChangeEvent is at obj_impl+0x8+0xB8+0x14 (Same with nfc/nfp interfaces). This therefore means +0xA4 in the input buffer will overwrite the handle returned by that cmd, allowing one to leak any handle with the specified value. This can be done with count=0x6. The object is large enough that this count will only overwrite data within the current object. However during the dtor it will use ptrs which were corrupted with this (located before the event), so one must avoid closing the session unless the input data included valid ptrs. | |||
| [[ | |||
| | This can be exploited by just using a 0xC0-byte (array_count=0x6) input buffer with Initialize where each u32 is the target nfc handle value, then using cmd GetAvailabilityChangeEventHandle to leak the handle. | ||
| | |||
nn:: | |||
[22.0.0+] This was fixed by clamping the count to a maximum of 0x4. | |||
| OOB datacopy into object state. Allows leaking arbitary [[NFC_services|handles]], including on [S2] (such as process-handle, sm, fsp-srv (remaining services can also be used via sm)). | |||
| [[22.0.0]] | |||
| | | [[22.0.0]] | ||
| [[ | | November 2021 | ||
| [[ | | March 17, 2026 | ||
| | | [[User:Yellows8|yellows8]] | ||
| | |} | ||
| [[User:Yellows8|yellows8]] | |||
|} | |||
== Internet Browser == | |||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
! | ! Summary | ||
! Description | ! Description | ||
! Fixed | ! Successful exploitation result | ||
! | ! Fixed in system version | ||
! Last system version this flaw was checked for | |||
! Timeframe this was discovered | ! Timeframe this was discovered | ||
! Public disclosure timeframe | ! Public disclosure timeframe | ||
! Discovered by | ! Discovered by | ||
|- | |- | ||
| | | CVE-2016-4657 | ||
| | | WebKit vuln discovered around August 2016. Most notably used in the iOS 9.3.X exploit. A simple PoC can be found [https://github.com/LiveOverflow/lo_nintendoswitch/blob/master/poc1.html here]. This was later exploited by [https://twitter.com/qwertyoruiopz Qwertyoruiop] using an adjusted version of his iOS 9.3 webkit exploit (others exploited this prior to then). | ||
| | |||
| [[2.1.0]] | |||
| [[2.0.0]] | |||
| | | Original: August 2016 | ||
| [[ | Switch: March 3rd-4th 2017 | ||
| | | | ||
| Everyone | |||
|- | |- | ||
| CVE-2017-7005 | |||
| WebKit type confusion. | |||
| | |||
| [[3.0.1]] | |||
| [[3.0.1]] | |||
| | |||
| | |||
| Everyone | |||
|- | |||
| CVE-2016-4622 | |||
| WebKit memory corruption bug. This bug was incorrectly re-introduced in [[4.0.0]]. See [http://www.phrack.org/papers/attacking_javascript_engines.html here] for a detailed write-up from the author. | |||
| | |||
| [[6.1.0]] | |||
| [[6.1.0]] | |||
| | |||
| | |||
| Everyone | |||
|- | |- | ||
| | | CVE-2018-4441 | ||
| | | WebKit memory corruption bug. See [https://bugs.chromium.org/p/project-zero/issues/detail?id=1685&desc=2 here]. | ||
| | |||
| [[7.0.0]] | |||
| [[7.0.0]] | |||
| | |||
| | |||
| Everyone | |||
| | |||
| | |||
| | |||
| | |||
| | |||
| | |||
|- | |- | ||
| | | Web-applets OpenSSL broken RNG | ||
| | | [[SPL_services|csrng]] access was added to web-applets with [12.1.0+]. Prior to that, csrng and nn::os::GenerateRandomBytes were not used (besides sdk heap code). | ||
nn::os::GetSystemTick is used to seed the OpenSSL RNG, among other data. Hence, it's probably (?) possible to bruteforce the RNG initial state, allowing predicting RNG output. | |||
The RNG code is wkcRandomNumbersPeer (peer_wkc nro), with the initialization code using GetSystemTick located in the func immediately before wkcGetTickCountPeer. The former is called from wkcOsslRandFilefReadPeer. wkcOsslRandFilefReadPeer is called for seeding the OpenSSL RNG. | |||
With [12.1.0+], wkcRandomNumberPeer/wkcRandomNumbersPeer wrap nn::os::GenerateRandomBytes. wkcCryptographicallyRandomValuesPeer was added which wraps nn::crypto::GenerateCryptographicallyRandomBytes. wkcOsslRandFilefReadPeer now calls nn::crypto::GenerateCryptographicallyRandomBytes instead of wkcRandomNumbersPeer. | |||
| Breaking web-applets OpenSSL RNG -> potentially predict RNG data (keys(?)) during TLS comms. | |||
| [[12.1.0]] | |||
| [[12.1.0]] | |||
| January 28, 2022 | |||
| October 8, 2024 | |||
| [[User:Yellows8|yellows8]], likely (?) others | |||
|} | |||
=== Whitelist === | |||
This section documents [[Internet_Browser|WebApplet]] whitelist issues in applications. These can be used to load your own browser content over plain HTTP, which then for example could be used for web-applet exploitation. | |||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
! Application | |||
! | |||
! Description | ! Description | ||
! Fixed with app version | |||
! Fixed | ! Newest app version this flaw was checked for | ||
! | |||
! Timeframe this was discovered | ! Timeframe this was discovered | ||
! Public disclosure timeframe | ! Public disclosure timeframe | ||
! Discovered by | ! Discovered by | ||
|- | |- | ||
| | | Sonic Mania | ||
| | | Originally this game launched web-applet with a plain-http URL for displaying the manual, this was later changed to https. Originally the whitelist only had 1 entry for a http URL, this was later replaced with various https-only URLs. | ||
| 1.04, unknown if fixed with an earlier update | |||
| 1.04 | |||
| January (?) 2022 | |||
| February 23, 2022 | |||
| [[User:Yellows8|yellows8]] | |||
|} | |||
== NintendoSDK == | |||
This section documents vulnerabilities for NSOs in NintendoSDK. | |||
This | === nnSdk === | ||
This section documents vulnerabilities for nnSdk (sdknso). | |||
{| class="wikitable" border="1" | |||
|- | |||
! Summary | |||
! Description | |||
! Successful exploitation result | |||
! Fixed in SDK [[System_Versions|version]] | |||
! Last SDK version this flaw was checked for | |||
! Timeframe this was discovered | |||
! Public disclosure timeframe | |||
! Discovered by | |||
|- | |||
| [[HID_services|hidbus]] GetJoyPollingReceivedData buffer overflow | |||
| hidbus GetJoyPollingReceivedData doesn't validate the u8 size used for memcpy, when copying the data to the output JoyPollingReceivedData. With 11.x, the size is now clamped to a maximum of 0x2C (regardless of polling-mode). Note that 0x2C is the data-size for JoyButtonOnlyPollingDataAccessor, the other polling-modes have a smaller size. | |||
The hid-sysmodule code which writes data here does handle it properly: size is clamped to a max size, and the data-read uses a fixed-size anyway (hence there's no way to trigger this sdknso vuln with the hid-sysmodule tmem writing code). | |||
This could only be exploited if one directly writes to the tmem when one has previously compromised hid-sysmodule, without using the normal tmem-writing func for this. | |||
There are only a few [[HID_services#ExternalDevices|apps]] which use hidbus. | |||
| | | Triggering a buffer overflow in an application which uses hidbus GetJoyPollingReceivedData, from a previously compromised hid-sysmodule. | ||
| | | 11.x.0 | ||
| | | 11.4.0 | ||
| | | March 2020 | ||
| | | December 3, 2020 | ||
| [[User:Yellows8|yellows8]] | | [[User:Yellows8|yellows8]] | ||
|- | |- | ||
| | | [[Profile_Selector|Profile Selector]] uninitialized input data | ||
| | | Originally unused regions of [[Profile_Selector]] UiSettings/UserSelectionSettings were not cleared prior to being sent to the applet. With 1.x.x these are now properly memset(). | ||
| Stack infoleak from user-process, sent to the applet. | |||
| 1.x.x | |||
| 11.4.0 | |||
| November-December 2019 | |||
| December 31, 2020 | |||
| [[User:Yellows8|yellows8]] | |||
|} | |||
=== NEX === | |||
This section documents client-side vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/NEX-Overview-(Game-Servers) NEX]. | |||
{| class="wikitable" border="1" | |||
| | |||
|- | |- | ||
! Summary | |||
! Description | |||
! Successful exploitation result | |||
! Fixed in version | |||
! Timeframe this was discovered | |||
! Public disclosure timeframe | |||
! Discovered by | |||
|- | |- | ||
| | | Buffer overflow in StringConversion::T2Char8 | ||
| | | StringConversion::T2Char8 is used to convert IP addresses from a platform-specific encoding to UTF-8. On the 3DS and Switch, the implementation is simply a strcpy. By sending a long IP address string, a buffer overflow can be triggered on the stack. The vulnerability can be triggered through the NAT traversal protocol. A blog post about this vulnerable can be found [https://reversing.live/hacking-hundreds-of-wii-us-at-once.html here]. | ||
| Stack overflow in any game that uses NEX for matchmaking | |||
| Fixed server-side | |||
| December, 2022 | |||
| May, 2024 | |||
| [https://github.com/kinnay Yannik] | |||
|} | |||
=== Pia === | |||
This section documents vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview Pia]. | |||
In v5.11.3 (exact starting version unknown) the fixes aren't present for the below vulns which were fixed in v5.9.3, while in v5.18.98 these are present (exact starting version unknown). This probably indicates that the vuln fixes were backported from a newer Pia version to v5.9.3. | |||
The Pia packet handlers are only active when the game is using multiplayer. LanProtocol is only active in the games which are actively using the LAN-mode option (not Ldn) - only certain games support LAN-mode. The LanProtocol Pia packet handler can be reached while in a lobby or searching for one. | |||
Most Pia packets require an active StationProtocol connection to be active with {InetAddr which the packet was received from}, otherwise the packet is filtered out. The only protocols which don't use filtering are the following: NatTraversalProtocol, LanProtocol, StationProtocol, LocalProtocol. | |||
Note that broadcast IP-dest Pia packets are accepted - this can be used to target every device on the network which is using Pia (which is really only useful with {above protocols} due to the filtering mentioned above, unless one also handles StationProtocol). | |||
{| class="wikitable" border="1" | |||
|- | |||
! Summary | |||
! Description | |||
! Successful exploitation result | |||
! Fixed in Pia version | |||
! Last Pia version this flaw was checked for | |||
! Timeframe this was discovered | |||
! Public disclosure timeframe | |||
! Discovered by | |||
|- | |- | ||
| | | nn::pia::session::RelayRouteManageJob::UpdateConnectionReport buffer overflow | ||
| | | nn::pia::session::RelayRouteManageJob::UpdateConnectionReport() checks that the input size is at least {value}, but there's no max size check. This is used to memcpy from the input to elsewhere - hence buf-overflow if size is too large. The dst buffer is allocated on the pead heap - this buffer is probably small. | ||
Note that there's various requirements before it would actually reach the memcpy, such as <code><nn::pia::session::Mesh::IsHost() const></code> must return true. | |||
This is called from nn::pia::session::MeshProtocol::ParseConnectionReport(). | |||
ParseConnectionReport uses a state ptr for object nn::pia::session::RelayRouteManageJob, it will return if not set. nn::pia::session::Mesh::Initialize handles setup for this, depending on an input field from nn::pia::session::Mesh::Setting. These settings originate from <code><nn::pia::session::Session::CreateInstance(nn::pia::session::Session::Setting const&)></code>, which is called by user-code with the needed settings. | |||
ParseConnectionReport is therefore only usable if the game explicitly enables the Relay functionality. | |||
In fixed versions immediately after the StationIndex validation it now does: <code>if(statefield+0x10<input_size) return;</code> | |||
| | | Heap buffer overflow triggered by a Pia MeshProtocol message sent to a host device. | ||
| | | v5.9.3, see above. | ||
| | | v5.9.1/v5.9.2/v5.9.3 | ||
| | | November 11, 2022 | ||
| | | November 15, 2022 | ||
| | | [[User:Yellows8|yellows8]] | ||
|- | |- | ||
| nn::pia::transport::UnreliableProtocol::Dispatch buffer overflow | | nn::pia::lan::LanProtocol::ParseSessionMessage buffer overflow | ||
| nn::pia::lan::LanProtocol::ParseSessionMessage() calls nn::pia::lan::LanSessionMessage::Deserialize() to deserialize the message payload data buffer into the LanSessionMessage object on stack. LanSessionMessage::Deserialize (among other things) memcpys data from the input buffer to the object, using an u32 from the input buffer - there is no size validation in Deserialize itself. | |||
There is a size check immediately after calling Deserialize() to verify <code>payloadsize=={u32val}+{constant}</code>, returning on fail - but this doesn't matter for too-large-size. | |||
In fixed versions Deserialize now does bounds checking, both for the minimum message size and clamping the memcpy size to a constant. An error is thrown if the clamped memcpy size is larger than the message size. The caller now checks the ret properly, previously it was ignored. | |||
Following the size check in ParseSessionMessage() it calls <code><nn::pia::session::Mesh::IsProcessingLeaveMesh() const></code>, returning if ret is false. | |||
Then it calls nn::pia::lan::LanProtocol::ReceivedFragmentData::Receive(), with the memcpy'd buffer/size from the above LanSessionMessage, and other fields from LanSessionMessage. This eventually memcpys the input buffer to object+{offset}+{chunksize_field}*inputu8, there is no validation for size or inputu8 (except for the above size check). Hence, if the u8 is large enough, this would result in a heap buffer overflow. | |||
In fixed versions ReceivedFragmentData::Receive added a bunch of validation before the memcpy. | |||
| Stack/heap buffer overflow triggered by a Pia LanProtocol message. | |||
| v5.9.3, see above. | |||
| v5.9.1/v5.9.2/v5.9.3 | |||
| November 14, 2022 | |||
| November 15, 2022 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |||
| nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation buffer overflow | |||
| <code><nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation(nn::pia::transport::ReceivedMessageAccessor const&)></code> This immediately returns if *(ReceivedMessageAccessor+16) is 0. Then the input data is deserialized. The input u64 array is deserialized to stack, the u8 arraycount field from input is not validated. | |||
Hence, stack buffer overflow. Note that there's similar loop code in nearby funcs, which do validate the count properly. | |||
In fixed versions the arraycount field is now validated. | |||
SessionProtocol uses ReliableSlidingWindow MessageHeader, with a maximum message size of 0x100. The allocated size used for the above u64 array is also 0x100-bytes. Hence, when triggering a buf overflow the data after the buffer is uncontrolled data from the SessionProtocol object. | |||
| Stack buffer overflow triggered by a Pia SessionProtocol message. | |||
| v5.9.3, see above. | |||
| v5.9.1/v5.9.2/v5.9.3 | |||
| November 14, 2022 | |||
| November 15, 2022 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |||
| Optional Pia packet encryption | |||
| Pia packet encryption is optional. If the encryption flag is disabled, the packet handler will accept it and skip crypto. | |||
In fixed versions immediately after grabbing a packet, it now checks the crypto flag. If it's plaintext the packet is dropped. | |||
This can be used to send a plaintext Pia packet without needing to handle encryption, especially useful if the session-key can't be obtained (online-play matchmaking). This could be combined with other vulns if wanted. | |||
| Sending a plaintext Pia packet without needing to handle encryption. | |||
| v5.9.3, see above. | |||
| v5.9.3 (and later versions) | |||
| | |||
| November 19, 2022 | |||
| | |||
|- | |||
| nn::pia::session::{JoinMeshJob/ProcessUpdateMeshJob}::SetStationDataList OOB read/write/vfunc-call | |||
| <code>nn::pia::session::JoinMeshJob::SetStationDataList</code>is called by <code>nn::pia::session::MeshProtocol::ParseJoinResponse(nn::pia::transport::ReceivedMessageAccessor const&)></code> with the ReceivedMessageAccessor buffer. | |||
SetStationDataList will update state and immediately return if the join was denied. It will also validate the num_mesh_stations field against state. ParseJoinResponse also essentially verifies that the message was received from the host device. | |||
The input buffer size is ignored. | |||
The num_fragments field must be value 1 or <=3 otherwise it will return, there's two seperate code blocks handling these. | |||
Other than the checks at the start, there's no validation for the index fields. So large enough values could result in OOB-reads. | |||
When handling multiple fragments, it will loop through the stationinfo list. There is no validation for the u8 count field or the baseindex field. It calls a vfunc from obj baseptr+index*{entrysize} with data from the buffer, where index starts with the above baseindex field. Afterwards, an u8 is copied into an u32 array (with certain versions an u16 is deserialized into an u16 array). | |||
<code>nn::pia::session::ProcessUpdateMeshJob::UpdateStationDataList</code> is (eventually) called from <code>nn::pia::session::MeshProtocol::ParseUpdateMesh</code>, which has similar issues to the above. | |||
Note that ParseJoinResponse/ParseUpdateMesh essentially require the message to be received from the host device. | |||
With fixed versions (v5.18.98, exact version unknown) various validation was added. Additional/updated validation was added in a later version (v5.31.0, exact version unknown). | |||
| OOB read/write / vfunc call where the object is selected by an OOB index, triggered by a Pia MeshProtocol message. | |||
| v5.18.98 and v5.31.0 (exact versions unknown). | |||
| v5.31.0 | |||
| November 18, 2022 | |||
| November 21, 2022 | |||
| [[User:Yellows8|yellows8]] | |||
|- | |||
| Insecure encryption | |||
| Originally Pia packets used AES-ECB encryption. As documented [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview here] it was later changed with v5.7.0 to AES-GCM. Each 0x10-byte block would have the same encrypted block output where the plaintext 0x10-byte data is the same. | |||
The mechanism for generating the Pia SessionKey for LAN has also changed over time. | |||
The [https://github.com/Kinnay/NintendoClients/wiki/LAN-Protocol LAN] non-Pia-encapsulated packets were also originally sent in plaintext, however at some point it was changed to mostly encrypted. | |||
| | |||
| AES-GCM fix: v5.7.0 | |||
| | |||
| | |||
| | |||
| | |||
|- | |||
| nn::pia::transport::UnreliableProtocol::Dispatch buffer overflow | |||
| <code>nn::pia::transport::UnreliableProtocol::Dispatch</code> memcpys data from the message into a list entry, without size validation. If the pia packet is the max size, it will only overwrite the 0xC-bytes which were written to immediately before the memcpy: the u32 size and the 8-byte StationAddress (depending on the version there can also be 4-byte padding after the size for alignment). | | <code>nn::pia::transport::UnreliableProtocol::Dispatch</code> memcpys data from the message into a list entry, without size validation. If the pia packet is the max size, it will only overwrite the 0xC-bytes which were written to immediately before the memcpy: the u32 size and the 8-byte StationAddress (depending on the version there can also be 4-byte padding after the size for alignment). | ||
However, nn::pia::transport::UnreliableProtocol::Receive will clamp the size from the list entry to the outbuf size when doing the memcpy. So this is probably useless. | However, nn::pia::transport::UnreliableProtocol::Receive will clamp the size from the list entry to the outbuf size when doing the memcpy. So this is probably useless. | ||
It's unknown whether there's a version where more data could be overwritten, and whether that would be useful. | It's unknown whether there's a version where more data could be overwritten, and whether that would be useful. | ||
This is fixed in v5.31.0, exact version unknown. The message is dropped if too large in Dispatch. | This is fixed in v5.31.0, exact version unknown. The message is dropped if too large in Dispatch. | ||
| Small buffer overflow triggered by a Pia UnreliableProtocol message. | | Small buffer overflow triggered by a Pia UnreliableProtocol message. | ||
| v5.31.0, exact version unknown. | | v5.31.0, exact version unknown. | ||
| v5.18.98/v5.31.0 | | v5.18.98/v5.31.0 | ||
| November 2022 | | November 2022 | ||
| November 29, 2022 | | November 29, 2022 | ||
| [[User:Yellows8|yellows8]] | | [[User:Yellows8|yellows8]] | ||
|- | |||
| Uncleared input structs for [[LDN_services|LDN]] | |||
| The Pia code using ldn CreateNetwork*/ConnectNetwork*/Scan doesn't properly memset the input data for SecurityConfig/ScanFilter (when keysize is less than 0x40 for the former). Hence, infoleak from games is sent to ldn (structs are located on stack, so stack data is leaked). This requires ldn compromise/mitm to obtain the leaked data - these are not sent over the network. | |||
With v6.20.1 (exact version unknown - fix isn't present in v5.32.0), the code using Scan* now clears the input ScanFilter properly. With v6.25.1 (exact version unknown - fix isn't present in v6.23.3), the code using CreateNetwork*/ConnectNetwork* now clears the input SecurityConfig properly. | |||
| Infoleak from games with LDN cmds, requires compromised sysmodule/mitm. | |||
| v6.20.1 and v6.25.1, exact versions unknown. | |||
| v5.32.0/v6.20.1/v6.23.3/v6.25.1 | |||
| | |||
| December 7, 2022 | |||
| | |||
|} | |||
=== ENL === | |||
This section documents vulnerabilities for [https://github.com/kinnay/NintendoClients/wiki/ENL-Protocol ENL]. | |||
A framework used by Nintendo games including Mario Kart 8 Deluxe, Splatoon 2 / 3, Mario Maker 2, and more. | |||
Fun fact, this library appears to re-use network code and concepts from older Nintendo titles such as Mario Kart 7 and some Wii multiplayer games. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Summary | |||
! Description | |||
! Successful exploitation result | |||
! Fixed in Enl version | |||
! Last Enl version this flaw was checked for | |||
! Timeframe this was discovered | |||
! Public disclosure timeframe | |||
! Discovered by | |||
|- | |||
| enl::TransportManager::updateReceiveBuffer_() nullptr deref | |||
| enl::TransportManager::updateReceiveBuffer_() is called when the ENL framework receives a PIA packet from a client, it will fully trust the ENL header which includes a "ContentTransporter" type (ID) and a length. | |||
The function will try to fetch the content transporter by ID using <code>enl::TransportManager::getContentTransporter(unsigned char const &)</code>, it returns NULL if there's no content transporter with the same ID | |||
*NOTE: The function may be inlined | |||
Then it will try to call a virtual method: <code>virtual size_t readyReceiveStream(enl::RamReadStream&, enl::Buffer*, size_t)</code>, dereferencing the pointer to fetch the vtable ptr | |||
[https://gist.github.com/Rambo6Glaz/c088e2ed7a12db08f6322e9f7a3c4911 Pseudocode of the function before it was fixed] | |||
| nullptr dereference triggered by an invalid content transporter type in the ENL header (it will crash the game/process) | |||
| Unknown | |||
| Depends on the game | |||
| Early April 2022 | |||
| November 16, 2022 | |||
| [[User:Rambo6Glaz|Rambo6Glaz]], [https://github.com/kinnay Yannik] (massive RE help) | |||
|} | |||
There's another one more interesting but it will have to wait a bit :) | |||
== Games == | |||
{| class="wikitable" border="1" | |||
|- | |||
! Game | |||
! Summary | |||
! Description | |||
! Impact | |||
! Fixed in version | |||
! Timeframe this was discovered | |||
! Public disclosure timeframe | |||
! Discovered by | |||
|- | |- | ||
| | | Mario Kart World | ||
| | | ASLR leak in application data | ||
| A memory address can be leaked by changing your username to something short, and hosting a network session in LAN mode (press L + R + Left Stick on the main menu to enable this). The memory address can be found in bytes 12 - 19 of the application data that is transmitted in response to a browse request. | |||
'''Note:''' there is more uninitialized data in the packet, but the memory address is probably the most interesting part. The vulnerability was fixed by clearing the application data with zeros, before filling in the information. | |||
[https://hackerone.com/reports/3463719 HackerOne report] | |||
This stack infoleak was also present in the [[LDN_services|ldn]] AdvertiseData. | |||
| A memory address can leaked (this is a requirement for many types of attacks). | |||
| 1.5.0 | |||
| December 12, 2025 | |||
| February 19, 2026 | |||
| [https://github.com/kinnay Yannik], yellows8 (ldn) | |||
|- | |- | ||
| Splatoon 3 | |||
| Anticheat Seed Randomization Weakness | |||
| This oversight of seed generation would allow an attacker to quickly compute all code hashes, and modify game code, while still producing a valid ch1 hash. | |||
| | |||
| | |||
| | |||
[https:// | [https://hackerone.com/reports/3042475 HackerOne report] | ||
| Allows an attacker to bypass the ch1 anti-cheat hashing mechanism. | |||
| | | 10.0.0 | ||
| | | March 17, 2025 | ||
| | | February 19, 2026 | ||
| hana2736 | |||
| | |||
| | |||
|} | |} | ||