Exploiting an Unbounded memcpy in Parallels Desktop

We discussed this vulnerability as part of our weekly podcast on 25 May 2022

Three bugs for one guest-to-host escape.

The first bug of the report is what is dubbed the “megasmash.” Basically a wild copy, you have an allocation of size+1. When exploited by providing the max int as size. The allocation will be asking for 0 bytes of space, but will ultimately attempt to copy size or the max int number of bytes. Such a massive copy will overflow into adjacent memory and beyond.

The second bug was an information leak which was used to break ASLR. This was pretty straight forward, one of the operations they could send to the handler for OTG opcodes would resize a QbyteArray to store 0x90 bytes, but it would only fill 0x20 bytes. Exposing the uninitalized 0x70 bytes of heap memory to the caller. This could be called multiple times to leak more information and leak whatever pointers were wanted.

Finally, there was a smaller stack-based overflow which is the third bug. This one an attacker could provide a maximum of 1024 UTF-16 characters that would be null terminated. There is no check if the string is actually null-terminated though, so by passing a string without a null terminator, it’ll actually copy beyond the bounds of the buffer until it reaches a null-byte. While they would run into a null-byte too soon to attack the stack pointer they were able to corrupt the msg.worker pointer which a pointer would be written to. Giving them a simple primitive to write an uncontrolled (pointer) value to any location.

This uncontrolled write primitive is what they used to actually make use of the megasmash bug from earlier. Normally such a massive copy would be challenging to exploit because the copy will quickly run into run-only memory or unmapped regions triggering a fault and a crash. Parallels however had registered an exception handler, which existed off in its own thread doing its thing. What they found was that if one could cause the exception handler to raise an exception then you’d end up in a loop where the exception handling thread is suspended, waiting on the exception handling thread to handle its exception (the specifics are a bit more involved but thats the gist of it). So they used the third bug, the uncontrolled write to overwrite a pointer that the exception thread would use, to effectively disable exception handling. So when their megasmash ran to corrupt other data, the exception it raised would just be ignored. A fun trick to make use of the wild copy for sure.

They used the megasmash to target some QtCore data, corrupting a function pointer, leading to PC control, a stack pivot and a rop chain later to call system and pop calc.