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.