This blog post details another savegame exploit found in VVVVVV, affectionately named (v*)hax. This post is purely for documentation, to download the exploit you can look here and for the exploit code you can check here.

The Save

The save files in VVVVVV are especially easy to pick apart, since the save files themselves are actually just XML files with a cool .vvv extension. Because of this, there are no checksums or other security measures used on them, and for the most part they are fairly straightforward in terms of modification since you also have nice tags to label what everything does. On a first load, the save will contain an unlock.vvv and a levels/ folder, in addition to this you also have levelstats.vvv, qsave.vvv for quick saves, tsave.vvv for teleporter saves, and a .vvv file for every user-made level saved in.

image

Exploitation

Based on a quick glance, there’s plenty of possible opportunities for exploitation: strings, arrays, and the file parsing itself could be exploited. Looking again at the files available to potentially exploit, however, shows that the first file loaded by VVVVVV is unlock.vvv. This file specifies the current version of the save, what has been unlocked, best times, trinkets, and other settings. Much like level .vvv files, it also contains several flag and byte arrays:

image

In this instance, however, the main topic of interest is the <besttimes> tag, due to it containing a large data array which could potentially be overflowed, as well as the data itself not being binary in nature. Doing a quick fuzz by writing in 255,254,253 several times past the usual array size immediately yielded a vector for potential ROP:

image
image

The array of values is loaded into unsigned 16-bit values starting from a given address and continuing however long the array is parsed. Since the .vvv file itself is allocated to it’s file size, this yields a very easy method of overflowing as much data as needed for exploitation. Even better, unlock.vvv reads to the stack. However, it reads very low on the stack, and this has one major disadvantage: the only way to control PC from a pop would be to exit the main loop, which simply doesn’t happen. However, there are still plenty of pointers to manipulate within the region given. Basic fuzzing did not reveal much of anything that could give an arbitrary write, unfortunately.

Investigating the level XML loading showed they also could be overflowed into other data. Initially I had investigated level save files with the purpose of allowing the game to still be playable, however in the process I found the perfect vector for ROP: The offset in which the <worldmap> array is parsed into is within the range that the unlock.vvv can overflow. While this came at the cost of keeping the game playable, this means that unlock.vvv can overflow a perfect stack portion except for this pointer, then instead of reading the <worldmap> array into the heap, the game will read it into the stack and immediately begin ROP from the parsing function itself. Testing however revealed that the area itself is zeroed to it’s proper array size (not the overflow size) before being parsed into, so the location to read into actually must be higher in the stack in order to prevent writing zeros into the stack before the actual data is read in.

Given ROP, it was somewhat trivial to load *hax, although not nearly as easy as supermysterychunkhax since there are limitations to the overflow. In this case, the size of the .text is extremely limited, however save space is plentiful, so a code payload can be copied into linear memory via ROP and then gspwned into an executable location and run, with the *hax payload also being read into memory during ROP and then loaded via the code payload.

Previous Versions and Conclusion

Looking at VVVVVV’s downloadable files on Nintendo’s eShop CDN revealed a v0 version which was released initially at launch, however it was quickly replaced by v1 a few months after the game launched in order to fix bugs in the game. Examining v0 for the same exploit revealed that while the game could still overflow the arrays in the .vvv files, the location on the stack of the pointers in which the level .vvv files are parsed into are higher on the stack than the portion which can be overflowed by unlock.vvv. That’s not to say that v0 isn’t exploitable, however the vector used in v1 will not work in v0, and because of this and the small availability window to even download v0, v0 is not supported by (v*)hax.

The fact that it only took at most 10 minutes to find an exploit in this game, however, does go to show that the security on many 3DS games can vary widely. While official Nintendo apps and first party games may implement their checks more carefully, some games will have no security at all. This can often make getting userland ROP a lot easier than it should be. Combined with a DMA exploit like gspwn, getting arbitrary code execution within almost any app is extremely simple after having exploited a previous game.

  1. shinyquagsire23 posted this