I am currently playing with pool-based memory corruption vulnerabilities. That’s why I wanted to program a PoC exploit for the vulnerability presented by Tarjei Mandt during his first talk “Kernel Pool Exploitation on Windows 7” . I think it’s a good exercise to start learning about pool overflows.
If you want to experiment with this vulnerability, you should read  and be sure to have a vulnerable system. I tested my exploit on a VM with Windows 7 32 bits with tcpip.sys 6.1.7600.16385. The Microsoft bulletin dealing with this vulnerability is MS10-058. It has been found by Matthieu Suiche  and was used as an example on Tarjei Mandt’s paper .
Triggering the flaw
An integer overflow in tcpip!IppSortDestinationAddresses allows to allocate a wrong-sized non-paged pool memory chunk. Below you can see the diff between the vulnerable version and the patched version.
So basically the flaw is merely an integer overflow that triggers a pool overflow.
1 2 3 4 5 6 7 8 9
You can reach this code using a WSAIoctl with the code SIO_ADDRESS_LIST_SORT using a call like this :
You have to pass the function a pointer to a SOCKET_ADDRESS_LIST (pwn in the example). This SOCKET_ADDRESS_LIST contains an iAddressCount field and iAddressCount SOCKET_ADDRESS structures. With a high iAddressCount value, the integer will wrap, thus triggering the wrong-sized allocation. We can almost write anything in those structures. There are only two limitations :
1 2 3 4 5 6
The copy will stop if those checks fail. That means that each SOCKET_ADDRESS has a length of 0x1c and that each SOCKADDR buffer pointed to by the socket address begins with a 0x17 byte. Long story short :
- Make the multiplication at IppSortDestinationAddresses+29 overflow
- Get a non-paged pool chunk at IppSortDestinationAddresses+3e that is too little
- Write user controlled memory to this chunk in IppFlattenAddressList+67 and overflow as much as you want (provided that you take care of the 0x1c and 0x17 bytes)
The code below should trigger a BSOD. Now the objective is to place an object after our vulnerable object and modify pool metadata.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Spraying the pool
Non paged objects
There are several objects that we could easily use to manipulate the non-paged pool. For instance we could use semaphore objects or reserve objects.
1 2 3
We are trying to overflow a pool chunk with a size being a multiple of 0x1c. As 0x1c*3=0x54, the driver is going to request 0x54 bytes and being therefore given a chunk of 0x60 bytes. This is exactly the size of an I/O completion reserve object. To allocate a IoCo, we just need to call NtAllocateReserveObject with the object type IOCO. To deallocate the IoCo, we could simply close the associate the handle. Doing this would make the object manager release the object. For more in-depth information about reserve objects, you can read j00ru’s article .
In order to spray, we are first going to allocate a lot of IoCo without releasing them so as to fill existing holes in the pool. After that, we want to allocate IoCo and make holes of 0x60 bytes. This is illustrated in the sprayIoCo() function of my PoC. Now we are able have an IoCo pool chunk following an Ipas pool chunk (as you might have noticed, ‘Ipas’ is the tag used by the tcpip driver). Therefore, we can easily corrupt its pool header.
If you want to debug a specific call to ExFreePoolWithTag and simply break on it you’ll see that there are way too much frees (and above all, this is very slow when kernel debugging). A simple approach to circumvent this issue is to use pool hit tags.
1 2 3 4 5 6 7 8 9
As you can see on the listing above, nt!PoolHitTag is compared against the pool tag of the currently freed chunk. Notice the mask : it allows you to use the raw tag. (for instance ‘oooo’ instead of 0xef6f6f6f) By the way, you are not required to use the genuine tag. (eg : you can use ‘ooo’ for ‘IoCo’) Now you know that you can ed nt!PoolHitTag ‘oooo’ to debug your exploit.
As the internals of the pool are thoroughly detailed in Tarjei Mandt’s paper , I will only be giving a glimpse at the pool descriptor and the pool header structures. The pool memory is divided into several types of pool. Two of them are the paged pool and the non-paged pool. A pool is described by a _POOL_DESCRIPTOR structure as seen below.
1 2 3 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
A pool descriptor references free memory in a free list called ListHeads. The PendingFrees field references chunks of memory waiting to be freed to the free list. Pointers to pool descriptor structures are stored in arrays such as PoolVector (non-paged) or ExpPagedPoolDescriptor (paged). Each chunk of memory contains a header before the actual data. This is the _POOL_HEADER. It brings information such as the size of the block or the pool it belongs to.
1 2 3 4 5 6 7 8 9 10
The basic idea of this attack is to corrupt the PoolIndex field of a pool header. This field is used when deallocating paged pool chunks in order to know which pool descriptor it belongs to. It is used as an index in an array of pointers to pool descriptors. Thus, if an attacker is able to corrupt it, he can make the pool manager believe that a specific chunk belongs to another pool descriptor. For instance, one could reference a pool descriptor out of the bounds of the array.
1 2 3
As there are always some null pointers after the array, it could be used to craft a fake pool descriptor in a user-allocated null page.
Non paged pool type
To determine the _POOL_DESCRIPTOR to use, ExFreePoolWithTag gets the appropriate _POOL_HEADER and stores PoolType (watchMe) and BlockSize (var_3c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Later, if ExpNumberOfNonPagedPools equals 1, the correct pool descriptor will directly be taken from nt!PoolVector. The PoolIndex is not used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Therefore, you have to make the pool manager believe that the chunk is located in paged memory.
Crafting a fake pool descriptor
As we want a fake pool descriptor at null address. We just allocate this page and put a fake deferred free list and a fake ListHeads.
When freeing a chunk, if the deferred freelist contains at least 0x20 entries, ExFreePoolWithTag is going to actually free those chunks and put them on the appropriate entries of the ListHeads.
1 2 3 4 5 6 7 8 9 10
It is interesting to note that this attack would not work with modern mitigations. Here are a few reasons :
- Validation of the PoolIndex field
- Prevention of the null page allocation
- NonPagedPoolNX has been introduced with Windows 8 and should be used instead of the NonPagedPool type.
- SMAP would prevent access to userland data
- SMEP would prevent execution of userland code
Payload and clean-up
A classical target for write-what-where scenarios is the HalDispatchTable. We just have to overwrite HalDispatchTable+4 with a pointer to our payload which is setupPayload(). When we are done, we just have to put back the pointer to hal!HaliQuerySystemInformation. (otherwise you can expect some crashes)
Now that we are able to execute arbitrary code from kernel land we just have to get the _EPROCESS of the attacking process with PsGetCurrentProcess() and walk the list of processes using the ActiveProcessLinks field until we encounter a process with ImageFileName equal to “System”. Then we just replace the access token of the attacker process by the one of the system process. Note that the lazy author of this exploit hardcoded several offsets :).
This is illustrated in payload().
Special thanks to my friend @0vercl0k for his review and help!
I hope you enjoyed this article. If you want to know more about the topic, check out the latest papers of Tarjei Mandt, Zhenhua Liu and Nikita Tarakanov. (or wait for other articles ;) )
You can find my code on my new github . Don’t hesitate to share comments on my article or my exploit if you see something wrong :)
 MS bulletin
 Kernel Pool Exploitation on Windows 7 – Tarjei Mandt’s paper. A must-read!
 Reserve Objects in Windows 7 – Great j00ru’s article!