Thursday, December 28, 2017

Convert Write-Where kernel exploits into arbitrary Write-What-Where exploit

This post is a follow up of my previous post: I realised that the technique I used there can be generalised.
  1. Currently it’s known that we can create PALETTE objects in the large session pool, and we can leak their address.
  2. We can allocate two PALETTE objects, one after the other, and even without spraying, they will be close enough (<0x1000000 bytes far) to each other - this came from my testing, and running this many times
  3. If we have a write - where vulnerability (where we can't control what we write), we can use that to modify the size of the PALETTE object by modifying 1 byte in the cEntries field, the only requirement is that we can precisely set the location, and it should be something other than 0. We will target the location marked with ** below:

    0: kd> dd fffff8cc44dc4000
    fffff8cc`44dc4000  fe0809ec ffffffff 00000000 00000000
    fffff8cc`44dc4010  26986580 ffffd687 00000501 **0003de
    fffff8cc`44dc4020  0096f8aa 00000000 00000000 00000000
    fffff8cc`44dc4030  00000000 00000000 00000000 00000000
    fffff8cc`44dc4040  00000000 00000000 00000000 00000000
    fffff8cc`44dc4050  00000000 00000000 00000000 00000000
    fffff8cc`44dc4060  00000002 00000001 00000000 00000000
    fffff8cc`44dc4070  00000000 00000000 44dc4088 fffff8cc

  4. Luckily the 7 bytes before and 8 bytes after that field are not so important, so if we smash them we will not cause a BSOD and we can continue to use the object. Essentially the following fields marked with red (also in the above memdump) in the PALETTE structure won't cause an issue if we overwrite them (anything before or after will cause a BSOD):
  5.  BASEOBJECT64      BaseObject;    // 0x00
     FLONG           flPal;         // 0x18
     ULONG32           cEntries;      // 0x1C
     ULONG32           ulTime;        // 0x20 
     HDC             hdcHead;       // 0x24
     ULONG64        hSelected;     // 0x28, 
  6. With modifying that byte to anything other then 0, we increased the size of our PALETTE to > 4 * 0x1000000, which means that if we have another one nearby (and as we saw at step 2 we can have), we can write to it with out-of-bound writes
  7. We overwrite the pFirstColor pointer of the 2nd PALETTE to point to our original PALETTE’s pFirstColor memory location, thus achieving a classic GDI read / write primitive —> We achieved arbitrary kernel read / write
Again, the practical use of this can be seen here:

This will also work if we have another type of vulnerability where we can decrement / increment a value at a memory location of our choice, we can achieve the same. We locate the byte above (**) as a target for decrementing or incrementing, we achieved the same.


  • The PALETTE itself, it won't work beyond Win10 RS3
  • If the write what we don't control is larger than 0x10 bytes, this can't be used as we overwrite other fields that will cause a BSOD

No comments: