What we need
We need to create a user object of bType=FREE (0x0) and bFlags= HANDLEF_DESTROY (0x1).
This is not possible directly, so we first focus on getting an object with the bFlag value equal to 0x1. For this purpose we create a Menu object, set it to a window, and then Destroy it. The internal reference count for the object did not reach zero because it is still being accessed by the window object, so it is not actually deleted but instead flagged as HANDLEF_DESTROY on the HandleEntry. This means the bFlag will equal to 0x1.
The bType value is directly associated to the Object Type. In the case of a menu object the value is 0x2 and there is no way of creating an object of type 0x0. So we focus on what ways we have to alter this value using some of the functions being called before destroying the WND object.
As you can probably remember from the PopupMenu structure shown before, it contains several WND objects, and one of the first actions performed when HMUnlockObject(pWnd) is called is to decrement the lockCount. So we simply set-up two fake WND objects in such a way that the lockCount field will be pointing to the HandleEntry->bType field. When each of those fake WND objects is destroyed it will actually perform a “dec” operation over the bType of our menu object, thus decrementing it from 0x2 to 0x0. We now have a bFlag of 0x1 and a bType of 0x0.
Using this little trick we are able to create a User object with the needed values on the HandleEntry.
First we will create a MenuObject and force it to be flagged as HANDLEF_DESTROY.
Then we will trigger the vulnerability, where xxxEndMenuState will get a reference to the menuState structure from a global thread pointer, and its value will be zero. So we map this address and create a fake MenuState structure at zero.
We have to remember that when we redirect execution to address 0, this memory also servers as a MenuState object. In particular the first field is a pointer to the PopupMenu object that we need to use. So what we do is to choose the address of this popup menu object in such a way that the least significant bytes of the address also represent a valid X86 jump opcode (e.g. 0x04eb translates to eb 04 in little endian memory ordering which represents a jump 4).
Once we achieve execution at ring 0 we patch the Enabled field on the _SEP_TOKEN_PRIVILEGES structure from the MOSDEF callback process in order to enable all the privileges for the process. We fix up the HandleEntry we modified before, and restore the stack in order to return after the PoolFree thus skipping the BSOD.
Once all of this is done we return to user-land, but now our MOSDEF process has all the privileges, this allows us to for example migrate to LSASS and get System privileges.