Show Notes

204 - Glitching the Wii-U and Integer Overflows

This article is about glitching the Wii-U’s read of One-Time Programmable (OTP) fuses into registers for verifying the boot process. Under normal circumstances, the boot ROM will verify the firmware stored in the NAND storage against a hash stored in fuses. However, if these fuses are all zeroes, it will disable the verification and just boot. So there is great incentive here to glitch the boot process to interpret these fuses as zero for both homebrew and brick recovery purposes incase the NAND degrades and the system can no longer boot due to failing the hash check.

Glitching was done by a combined power + reset glitch attack. By holding reset and inserting a very small reset pulse at a delayed offset, they could trigger a partial reset. This initially didn’t yield too much, the signature check could be bypassed but the boot would fail to continue executing. But by also under-volting the chip and tweaking with glitch parameters, they were able to brownout the OTP fuse read and get it read as zeroes.

This can allow for a homebrew boot1, which the author is looking to take advantage of to kickstart an open-source modchip project.

A fun bug, likely stemming from misunderstanding the return value from an snprintf call. Unfortunately (for us, good for security) only seems to be useful for a denial of service attack.

The httpServer function has a few cases where it seems to work as a sort of reverse proxy, reading in part of the request and crafting a new request that it’ll presumable send on. This happens when a request path becimes with /stats/ or /Security_Services/.

In these cases, it has a 1024 byte buffer, calls snprintf to write the HTTP verb (method) and request path to the buffer. It takes note of the return value and subtracts it from 1024 to track how many more bytes it can still write to the buffer. Then calls snprintf again to write the HTTP version string. This may have worked when using sprintf and sized format specifiers like %1024s because sprintf returns the number of bytes written to the buffer. However, snprintf is a bit more tricky, it’ll return the number of bytes written assuming it had space to write everything. So if it didn’t have space, it still returns the number of bytes it would have written. This might seem a bit weird but using snprintf with a 0 byte buffer is a common trick to find out how much space you need to allocate for a dynamic string.

Because snprintf isn’t necessarily returning how many bytes were written it can actually return a value larger than the given limit of the buffer, so after the first snprintf call when it adjusts the reamining buffer size, by subtracting what was written, that value can wrap negative resulting in the second snprintf call being calls with overly permissive bounds and an attacker can write well out of bounds on the stack. With canaries in play, and no other interesting data it seems most likely that this can only cause a crash.

A local privileged escalation in bthport.sys the Windows bluetooth bus driver. The vulnerability exists in the Service Discovery Protocol (SDP). A usemode application can interact with the driver through IOCTLs that exist to add and remove service records, and query for service records. Ever service as a series of service attributes, which are made up of a 16bit ID, and an indeterminate length attribute value. A service record can have as many attributes as it wants as they are saved in a doubly-linked list without a fixed size.

The vulnerability is in the SDP’s implementation of ServiceAndAttributeSearch. As the name implies this is used to search for a service containing a specific attribute ID, if found it will return the entire service record (all attributes). To do so it first determines how large of a buffer it needs by iterating over the attributes incrementing the size for each attribute, it does so without checking for an integer overflow and as such, if a service has several large attributes, the size value can overflow. So when the applications goes to allocate a buffer it will be too small for the data that will eventually be written into the buffer.

A local privileged escalation in bthport.sys the Windows bluetooth bus driver. The vulnerability exists in the Service Discovery Protocol (SDP). A usemode application can interact with the driver through IOCTLs that exist to add and remove service records, and query for service records. Ever service as a series of service attributes, which are made up of a 16bit ID, and an indeterminate length attribute value. A service record can have as many attributes as it wants as they are saved in a doubly-linked list without a fixed size.

The vulnerability is in the SDP’s implementation of ServiceAndAttributeSearch. As the name implies this is used to search for a service containing a specific attribute ID, if found it will return the entire service record (all attributes). To do so it first determines how large of a buffer it needs by iterating over the attributes incrementing the size for each attribute, it does so without checking for an integer overflow and as such, if a service has several large attributes, the size value can overflow. So when the applications goes to allocate a buffer it will be too small for the data that will eventually be written into the buffer.

I thought this was an excellent post when it came to explaining the exploitation strategy, and has it dealt with encrypted pointers the exploitation was pretty cool to see documented. However I did have some problems following on the actual vulnerability details.

Getting started though, this is a sandbox escape, it is assuming you have code execution within the Adobe Sandbox, and now the goal is to exploit the Adobe Sandbox Broker which is the middle-man between the sandbox and any outside information and is a more privileged application. Communication between the sandbox and the broker happens through a 2MB piece of shared memory, the broker tracks the state of its processing of any message in there, and the sandbox can send a signal to the broker when it has written something new to be processed.

Vulnerability

The broker provides a number of cross-calls which are calls exposed to the sandboxed process and end up calling external services. Of interest here is the Windows Crypto Provider, a set of libraries that provide common cryptographic algorithms. The broker provides a number of Crypto related cross-calls that call into the Crypto Provider Service (CryptSP). Specifically it is the CryptImportKey() cross-call that calls the CryptSP::CryptImportKey() method, eventually leading to ImportOpaqueBlob()the vulnerable function in rsaenh.dll. When importing a key, ImportOpaqueBlob will a couple values from the blob, which are used in some arithmetic to determine how large of a buffer to allocate. Without any integer overflow checks, a crafted blob could overflow that size to be allocated resulting in too small of an allocation and a linear write into adjacent memory

Exploitation

Being a security minded component, the usual security features are enabled like ASLR and DEP, there is also Control Flow Guard, and CryptSP context object, uses an encrypted pointer. Looking at the CryptSP context object that is necessary to make CryptSP calls. It is instantiated by a call to CryptAcquireContext() which returns the context object. The context object itself is a simple C structure containing a number of function pointers to exposed methods and the “real” provider object that is used internally, which is stored with an encrypted pointer. When it calls any of the provider methods, the first argument is always the encrypted pointer.

Arbitrary Function Call - This provides the first primitive of note, corrupting the context object you can corrupt the function pointer and encrypted pointer giving you control over a function call target and the first argument.

Identifying Corrupting Objects - The Broker provides access to the CryptGenRandom() cross-call, which is supposed to return a random value, but one can use the overflow to replace its function pointer with a function that returns a known value. Then call CryptGenRandom on all of the context objects in the shared memory block to see which one returns the known value. This is a nice trick to, atleast in part, avoid the need to break ASLR. Instead you spray many objects, and corrupt one at random, then detect which object you corrupted.

Lastly I like the trick used here to deal with the encrypted pointer as first argument setup. They target the CryptReleaseContext() cross-call, which calls CPReleaseContext with the encrypted pointer. Overwrite the CPReleaseContext function pointer with WinExec and the for the argument you overwrite the encrypted pointer object with the pointer to a previously corrupted context object with your desired string as the first contents. So you still use an encrypted object, its just that its encrypted and pointing to data you control, pretty cool. Sorry if I didn’t do this trick justice trying to explain it here, but I think they do a solid job in the original post. They also dive more into some of the spraying techniques and more details about exploitation that I’ve glossed over.