Toggle menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Soundhax 3DS: Difference between revisions

From GameBrew
No edit summary
No edit summary
Line 3: Line 3:
| image = https://dlhb.gamebrew.org/3dshomebrew/Soundhax.png|250px
| image = https://dlhb.gamebrew.org/3dshomebrew/Soundhax.png|250px
| type = Exploits
| type = Exploits
| version=unknown
| version=2020
| lastupdated = 2020/12/09
| lastupdated = 2020/12/09
| licence = Mixed
| licence = Mixed
Line 13: Line 13:
<youtube>Uysb6oQ7Bag</youtube>
<youtube>Uysb6oQ7Bag</youtube>


== Soundhax ==
==Soundhax==
Free 3DS Primary Entrypoint <= 11.3. A heap overflow in tag processing leads to code execution when a specially- crafted m4a file is loaded by Nintendo 3DS Sound. This bug is particularly good, because as far as I can tell it is the first ever homebrew exploit that is free, offline, and works on every version of the firmware for which the sound app is available.
 
A heap overflow in tag processing leads to code execution when a specially- crafted m4a file is loaded by Nintendo 3DS Sound.
 
This bug is particularly good, because as far as I can tell it is the first ever homebrew exploit that is free, offline, and works on every version of the firmware for which the sound app is available.
 
== Regions and Versions ==
== Regions and Versions ==
{|
{|
! Version
! Version
! N3DS
! N3DS/N2DS
! O3DS/2DS
! O3DS/2DS
|-
|-
| US 3.0-11.2
| US 1.0-11.3
| ✓
| ✓
| ✓
| ✓
|-
|-
| JPN 3.0-11.2
| JPN 1.0-11.3
| ✓
| ✓
| ✓
| ✓
|-
|-
| EUR 3.0-11.2
| EUR 1.0-11.3
| ✓
| ✓
| ✓
| ✓
|-
|-
| KOR 9.6-11.2
| KOR 4.0-11.3
| ✓
| ✓
| ✓
|-
| CHN ?-11.2
| ✗
| ✗
|-
| TWN ?-11.2
| ✗
| ✗
|-
| If your box is checked, then put [https://smealum.github.io/3ds/#otherapp otherapp.bin] on the root of your SD card along with soundhax.m4a and launch the song from the sound player.
|
|
|-
| It can be used along [https://github.com/Pirater12/pre9otherapp pre9otherapp] to launch an arm9 payload from the sd card on pre 9.0 firms(4.0 - 9.2).
|
|
|-
| The bug seems to exist on 2.X sound app too but the rop gadgets and the va to pa translation function need to be updated to make it work.(This was confirmed by installing a 2.1 sound app on 4.1).
|
|
|}
== Status for KOR/CHN/TWN ==
{|
! Status
! KOR
! CHN
! TWN
|-
| bug confirmed
| ✓
| ✓
| ✗
| ✗
|-
|-
| sound constants
| CHN 4.0-11.3
| N/A
| ✓
| ✓
| ✗
| ✗
|-
|-
| stage2 payload constants
| TWN 4.1-11.3
| N/A
| ✓
| ✓
| ✗
| ✗
|}
|}
'''All existing versions of Nintendo 3DS Sound prior to Nintendo fixing the vulnerability are now supported'''.
If your box is checked, then put [https://smealum.github.io/3ds/#otherapp otherapp.bin] on the root of your SD card along with soundhax.m4a and launch the song from the sound player.
It can be used along [https://github.com/hax0kartik/pre9otherapp pre9otherapp] to launch an arm9 payload from the SD card on pre 9.0 firms (2.1 - 9.2).
== Installation ==
== Installation ==
# Download the relevant soundhax-region-console.m4a file for your device.
 
# Download the relevant soundhax-region-console-firmware.m4a file for your device.
# Save the soundhax song file and copy to the root of your SD.
# Save the soundhax song file and copy to the root of your SD.
# Download the [https://smealum.github.io/3ds/ otherapp payload] for your 3DS version, rename it to <code>otherapp.bin</code>, and copy it to the root of the SD card.
# Download the [https://smealum.github.io/3ds/ otherapp payload] for your 3DS version, rename it to <code>otherapp.bin</code>, and copy it to the root of the SD card.
# Download the [https://smealum.github.io/ninjhax2/starter.zip Homebrew Starter Kit] and unzip to the root of the SD card (if it is not there already).
# Download the [https://github.com/fincs/new-hbmenu/releases/latest Homebrew Menu] and place <code>boot.3dsx</code> in the root of the SD card (if it is not there already).
# Insert the SD card into the 3DS and start Nintendo 3DS Sound.
# Insert the SD card into the 3DS and start Nintendo 3DS Sound.
# Locate your new song and play it to start the Homebrew Launcher! Fixing the annoying bird: Click through all of the bird tips then close the app normally. When you exploit it it doesn't save the fact that you've opened the app before, so closing and reopening normally seems to fix this.
# Locate your new song and play it to start the Homebrew Menu!
 
Fixing the annoying bird: Click through all of the bird tips then close the app normally. When you exploit it it doesn't save the fact that you've opened the app before, so closing and reopening normally seems to fix this.
 
== Build ==
 
Install [https://python.org Python 2.7] and [https://devkitpro.org/wiki/Getting_Started devkitARM].
 
Then run <code>python exp.py &lt;usa/eur/jpn/kor/chn/twl&gt; &lt;new/old&gt; &lt;pre21/v21and22/v3xand4x/post5&gt;</code> to generate <code>soundhax-*.m4a</code>.
 
== Build ==
== Build ==
Install [https://python.org Python 2.7] and [https://sourceforge.net/projects/devkitpro/ devkitpro]. Then run <code>python exp.py &lt;usa/eur/jpn/kor&gt; &lt;new/old&gt; &lt;pre5/post5&gt;</code> to generate <code>soundhax-*.m4a</code>.
Install [https://python.org Python 2.7] and [https://sourceforge.net/projects/devkitpro/ devkitpro]. Then run <code>python exp.py &lt;usa/eur/jpn/kor&gt; &lt;new/old&gt; &lt;pre5/post5&gt;</code> to generate <code>soundhax-*.m4a</code>.
Line 92: Line 80:
3DS Sound mallocs a buffer of 256 bytes to hold the name of song as described in its mp4 atom tags. This is sensible since it's the maximum allowed size according to the spec. When parsing an ascii title, <code>strncpy(dst, src, 256)</code> is used, which is safe and correct. However, because unicode strings contain null bytes, rather than using a unicode <code>strncpy</code> variant, the application simply <code>memcpy</code>s the name bytes onto the heap using the user provided size, which can be arbitrarily large.
3DS Sound mallocs a buffer of 256 bytes to hold the name of song as described in its mp4 atom tags. This is sensible since it's the maximum allowed size according to the spec. When parsing an ascii title, <code>strncpy(dst, src, 256)</code> is used, which is safe and correct. However, because unicode strings contain null bytes, rather than using a unicode <code>strncpy</code> variant, the application simply <code>memcpy</code>s the name bytes onto the heap using the user provided size, which can be arbitrarily large.
=== Exploit ===
=== Exploit ===
I overflow my data onto the next heap chunk, which lets me fully control the malloc header of that chunk, which happens to be allocated at the time of the overflow. When that chunk is freed, a heap unlink is performed, which allows me to do an arbitrary write. This means I can write a dword to the stack and control PC. Unfortunately, there aren't any usable gadgets (trust me, I looked), so I had to use a more advanced technique to exploit the bug. I used the arbitrary write to overwrite the free list header with a stack address, while setting the start and end fields of the chunk being freed to cause the block to appear undersized, thus causing it to not be added to the free list and so the stack address I just wrote is used on the next malloc. Because malloc jumps through the free list looking for a suitable block, I had to find a stack address at which there appears to be a valid heap chunk header with a large enough size for the requested allocation and null pointers for the next and prev entries in the list, so that my stack chunk is chosen as the 'best' one. Once all of these conditions are met, the next malloc returns the stack address as the 'heap' location to write my next tag data, which lets me turn the arbitrary write primitive into ROP. From there I use the gspwn GPU exploit to write my stage2 shellcode over the text section of the sound process, before finally jumping to it. In summary, the process looks like this: heap overflow -&gt; arbitrary write to free list -&gt; stack overflow -&gt; gspwn -&gt; code execution
I overflow my data onto the next heap chunk, which lets me fully control the malloc header of that chunk, which happens to be allocated at the time of the overflow. When that chunk is freed, a heap unlink is performed, which allows me to do an arbitrary write. This means I can write a dword to the stack and control PC.
 
Unfortunately, there aren't any usable gadgets (trust me, I looked), so I had to use a more advanced technique to exploit the bug. I used the arbitrary write to overwrite the free list header with a stack address, while setting the start and end fields of the chunk being freed to cause the block to appear undersized, thus causing it to not be added to the free list and so the stack address I just wrote is used on the next malloc.
 
Because malloc jumps through the free list looking for a suitable block, I had to find a stack address at which there appears to be a valid heap chunk header with a large enough size for the requested allocation and null pointers for the next and prev entries in the list, so that my stack chunk is chosen as the 'best' one.
 
Once all of these conditions are met, the next malloc returns the stack address as the 'heap' location to write my next tag data, which lets me turn the arbitrary write primitive into ROP.
 
From there I use the gspwn GPU exploit to write my stage2 shellcode over the text section of the sound process, before finally jumping to it.
 
In summary, the process looks like this:
 
heap overflow -> arbitrary write to free list -> stack overflow -> gspwn -> code execution
 
== Thanks ==
== Thanks ==


Subv and Citra authors
* Subv and Citra authors - for help emulating sound, this was invaluable
 
* plutoo - stage 2 shellcode
* for help emulating sound, this was invaluable plutoo
* yellows8 - help with gpu address translation for gspwn, initial JPN support, finished KOR support
* stage 2 shellcode yellows8 - help with gpu address translation for gspwn, initial JPN support, finished KOR support smea - homebrew launcher d3m3vilurr
* smea - homebrew launcher
* EUR, JPN, partial KOR support TuxSH - O3DS offset Konng
* d3m3vilurr - EUR, JPN, partial KOR support
* Testing EUR payloads #cakey - advice and support PPP
* TuxSH - O3DS offset
* teaching me everything I know geohot, comex, j00ru, loki, project zero - inspiring me to pursue bug hunting
* Konng - Testing EUR payloads
* cakey - advice and support
* PPP - teaching me everything I know
* geohot, comex, j00ru, loki, project zero - inspiring me to pursue bug hunting

Revision as of 10:37, 16 September 2021

Template:Infobox-3DS-Homebrews

Soundhax

A heap overflow in tag processing leads to code execution when a specially- crafted m4a file is loaded by Nintendo 3DS Sound.

This bug is particularly good, because as far as I can tell it is the first ever homebrew exploit that is free, offline, and works on every version of the firmware for which the sound app is available.

Regions and Versions

Version N3DS/N2DS O3DS/2DS
US 1.0-11.3
JPN 1.0-11.3
EUR 1.0-11.3
KOR 4.0-11.3
CHN 4.0-11.3 N/A
TWN 4.1-11.3 N/A

All existing versions of Nintendo 3DS Sound prior to Nintendo fixing the vulnerability are now supported.

If your box is checked, then put otherapp.bin on the root of your SD card along with soundhax.m4a and launch the song from the sound player.

It can be used along pre9otherapp to launch an arm9 payload from the SD card on pre 9.0 firms (2.1 - 9.2).

Installation

  1. Download the relevant soundhax-region-console-firmware.m4a file for your device.
  2. Save the soundhax song file and copy to the root of your SD.
  3. Download the otherapp payload for your 3DS version, rename it to otherapp.bin, and copy it to the root of the SD card.
  4. Download the Homebrew Menu and place boot.3dsx in the root of the SD card (if it is not there already).
  5. Insert the SD card into the 3DS and start Nintendo 3DS Sound.
  6. Locate your new song and play it to start the Homebrew Menu!

Fixing the annoying bird: Click through all of the bird tips then close the app normally. When you exploit it it doesn't save the fact that you've opened the app before, so closing and reopening normally seems to fix this.

Build

Install Python 2.7 and devkitARM.

Then run python exp.py <usa/eur/jpn/kor/chn/twl> <new/old> <pre21/v21and22/v3xand4x/post5> to generate soundhax-*.m4a.

Build

Install Python 2.7 and devkitpro. Then run python exp.py <usa/eur/jpn/kor> <new/old> <pre5/post5> to generate soundhax-*.m4a.

Writeup

The Bug

3DS Sound mallocs a buffer of 256 bytes to hold the name of song as described in its mp4 atom tags. This is sensible since it's the maximum allowed size according to the spec. When parsing an ascii title, strncpy(dst, src, 256) is used, which is safe and correct. However, because unicode strings contain null bytes, rather than using a unicode strncpy variant, the application simply memcpys the name bytes onto the heap using the user provided size, which can be arbitrarily large.

Exploit

I overflow my data onto the next heap chunk, which lets me fully control the malloc header of that chunk, which happens to be allocated at the time of the overflow. When that chunk is freed, a heap unlink is performed, which allows me to do an arbitrary write. This means I can write a dword to the stack and control PC.

Unfortunately, there aren't any usable gadgets (trust me, I looked), so I had to use a more advanced technique to exploit the bug. I used the arbitrary write to overwrite the free list header with a stack address, while setting the start and end fields of the chunk being freed to cause the block to appear undersized, thus causing it to not be added to the free list and so the stack address I just wrote is used on the next malloc.

Because malloc jumps through the free list looking for a suitable block, I had to find a stack address at which there appears to be a valid heap chunk header with a large enough size for the requested allocation and null pointers for the next and prev entries in the list, so that my stack chunk is chosen as the 'best' one.

Once all of these conditions are met, the next malloc returns the stack address as the 'heap' location to write my next tag data, which lets me turn the arbitrary write primitive into ROP.

From there I use the gspwn GPU exploit to write my stage2 shellcode over the text section of the sound process, before finally jumping to it.

In summary, the process looks like this:

heap overflow -> arbitrary write to free list -> stack overflow -> gspwn -> code execution

Thanks

  • Subv and Citra authors - for help emulating sound, this was invaluable
  • plutoo - stage 2 shellcode
  • yellows8 - help with gpu address translation for gspwn, initial JPN support, finished KOR support
  • smea - homebrew launcher
  • d3m3vilurr - EUR, JPN, partial KOR support
  • TuxSH - O3DS offset
  • Konng - Testing EUR payloads
  • cakey - advice and support
  • PPP - teaching me everything I know
  • geohot, comex, j00ru, loki, project zero - inspiring me to pursue bug hunting

Advertising: