Kernel Pool Overflow Exploitation in Real World: Windows 10
- Real safe linking / unlinking
- PoolIndex Validation: The pool index overwrite attack is not a thing anymore
- NonPagedPoolNx: There is a new type of pool which is basically NonPagedPool with NX activated. Windows uses it by default instead of NonPagedPool.
- SMEP: Supervisor Mode Execution Prevention
- MIN_MAP_ADDR: the 0x1000 first bytes of the memory are reserved and can't be allocated. This prevent the exploitation of Null Dereference vulnerabilities. This mitigation has been reverted on Windows 7 and Vista on x64 architectures
- The NtQuerySystemInformation() leak can't be used anymore in low integrity level (usually browser sandboxes)
- The process pointer is now encoded with a cookie:
- When the chunk is allocated, the process pointer is encoded: ExpPoolQuotaCookie XOR ChunkAddress XOR ProcessPointer
- When the chunk is free, the process pointer is used as a canary and encoded: ExpPoolQuotaCookie XOR ChunkAddress
- The process pointer, after being decoded, has to points in kernel land, or it will trigger a bugcheck.
- The PoolCookie: Because we need to properly encode the pointer
- The address of the chunk we overflow: Because we need to properly encode the pointer
- Some arbitrary data in kernel land at a known address: Even if we properly encode the pointer, it needs to point in kernel land but also on a fake structure we crafted.
HANDLE WINAPI CreatePrivateNamespace( _In_opt_ LPSECURITY_ATTRIBUTES lpPrivateNamespaceAttributes, _In_ LPVOID lpBoundaryDescriptor, _In_ LPCTSTR lpAliasPrefix );A few things are interesting:
- It returns a handle. Pretty normal, since it's an object. But it means we can leak the directory object's address in the PagedPool.
- The second argument is a Boundary descriptor. It has to be unique, so you can create it with the function CreateBoundaryDescriptor:
HANDLE WINAPI CreateBoundaryDescriptor( _In_ LPCTSTR Name, _In_ ULONG Flags );
- takes a name
gives this chunk in the PagedPool:
The name "Hello World !" is stored at object_address + 0x1A8And it seems to have no limits on the name:
Here, the chunk size is two times bigger, just to contains the name of the Boundary Descriptor! By the way, since the size of this object is controllable, it becomes a great tool to spray the PagedPool... Anyway, here we are, we can put some arbitrary data in kernel land, and we can have its address using the NtQuerySystemInformation leak! 4) The Pool Cookie This is getting intense. The ExpPoolQuotaCookie is generated at boot and has the size of a pointer: 8 bytes on x64 architectures. Its entropy is good, so there is no way to guess or compute it. At first look, the only way to leak the pool cookie is to have an arbitrary read, which is a pretty big and rare vulnerability. So I studied where the ExpPoolQuotaCookie is used: When a pool chunk is used in the quota management of a process, the Quota Bit is set in its PoolType, and there is a pointer encoded in the last 8 bytes of its pool header (x64 version):
But this is the case when the chunk is allocated ! When this chunk is freed, the chunk is a bit different:
Here, the Process Billed value is only the Pool Cookie xored with the address of the chunk! This value is used as a canary, to detect overflow and attacks on the pool. But this is interesting for us; using an advanced pool spraying, we can easily know the address of a chunk. Thus, if we manage to read the Process Billed value, we might be able to get the Pool Cookie! So I imagine the following attack:
- Using a pool spraying, we allocate some chunk we control ; we know their address and we're able to free them at anytime.
- We will first free one of the chunk...
- Then free the chunk just before
- Just after, we will reallocate a chunk at the exact same place of the two previous chunk. We can use an IOCTL to do this, and make sure the SystemBuffer is allocated here !
- Even with the free and reallocation of the chunks, the former pool header is not overwritten ! It means the value "Pool Cookie XOR ChunkAddress" is still in the data of our chunk !
Here, we can imagine an IOCTL that will return a little bit more data than it writes: an Out-Of-Bounds read! With the tiniest OOB read, we could manage to leak the Pool Cookie. So we went from an arbitrary read to a OOB read to leak the Pool Cookie, which is way more common. And I'm saying this for a reason ; I found an OOB read in the very same vulnerable driver! 4.1) About CVE-2017-7441 Here is the pseudo code of the vulnerable IOCTL with code 0x22E1C0:
So the driver calls the function RtlLookupElementGenericTableAvl with our input in the SystemBuffer. If the function succeed, it will copy the return of the function in the SystemBuffer using memcpy. But this time, the size is checked before the copy, so the memcpy is not a problem. Though there is an error in the calculation of how many bytes the driver is written, and it returns 2 excess bytes. If I want to reach the vulnerable code, I need the RtlLookupElementGenericTableAvl function to success and be able to control at least the length of the value it returns. The only way if found to do this is by writing the current process id in the SystemBuffer; The RtlLookupElementGenericTableAvl function works fine and returns the path to the executable of the current process as value. I can more or less control the length of the path to my executable; the maximum length of a path on Windows is 255 bytes. We just have to trigger the vulnerability 4 times by creating 4 different process with 4 different executables (with various path size) in order to leak the 8 bytes of the Pool Cookie! 5) Conclusion We have everything we needed to convert our Windows 7 exploit into a Windows 10 exploit : Exploiting a pool overflow in Windows 10 is one minor leak away from Windows 7! The Pool Spraying and the NtQuerySystemInformation leak gives to an attacker too much knowledge on the kernel state, making attacks on the pool still reliable. You can find the source of the exploit on my github 4]. 6) References  Windows 8 Heap internals  Kernel Pool Exploitation on Windows 7  Pool Spraying article  Source code of the exploit Research, exploit and article by BAYET Corentin.