Thursday, September 14, 2017

Windows kernel pool spraying fun - Part 3 - Let's make holes

Maybe I should have started this whole series with some explanation. I want to make some scripts that can help with making Windows kernel exploit development faster, and my first run is with pool spraying. Also, if you never read about kernel pool overflows, and exploiting them with pool spraying, maybe read this:


Now that we have a decent list of kernel object sizes (and the script can be run on other platforms, although probably I need to make some changes for x64 architecture) we can 'automate' the spraying and hole creation process, if we know what is the hole size we require.

Basically:
  1. Once we analyzed the vulnerability we will know what is the object / buffer size the driver will allocate in the pool
  2. We need to control the placement of that allocation, so we need to prepare a given size hole in the pool, so that the kernel will allocate the new object there
  3. If we know the size, we can simply calculate what kind of objects are good for our spraying and how many of them will need to be free up
  4. If we know all of that we can spray the kernel, and make a hole
We will need info about the object and pool headers what we overwrite with the overflow, but I will deal with that later, as it's not required for the hole creation. I might be wrong, but with some preparation I hope that the overwriting data can be automatically generated as well. For now, I just want to make holes with a given size. So I made a script for this, which is available here (please keep in mind that it's hardcoded for Win7 SP1 x86):


It will ask for the hole size you want, and do the spraying, freeing up the space and showing that area in WinDBG. Also note that it still uses the local kernel debugger, where we can't set breakpoint, so there is some race condition, when we issue the !pool command, as some other kernel process can allocate in the free space. The reason I still on local kernel debugging is that it's much simpler now for the demonstration. When I get to actual exploit demo, I will need to have remote debugging, but I can use the functions I demo here. So here is the output:

lkd> !py c:\users\csaby\desktop\spray_helper.py
Give me the size of the hole in hex: 440
Process: 8572bd40
Object location: 857e15f0
Pool page 857e15f0 region is Nonpaged pool
 857e1000 size:   40 previous size:    0  (Allocated)  Even (Protected)
 857e1040 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1080 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e10c0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1100 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1140 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1180 size:   40 previous size:   40  (Free )  Even (Protected)
 857e11c0 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1200 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1240 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1280 size:   40 previous size:   40  (Free )  Even (Protected)
 857e12c0 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1300 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1340 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1380 size:   40 previous size:   40  (Free )  Even (Protected)
 857e13c0 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1400 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1440 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1480 size:   40 previous size:   40  (Free )  Even (Protected)
 857e14c0 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1500 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1540 size:   40 previous size:   40  (Free )  Even (Protected)
 857e1580 size:   40 previous size:   40  (Free )  Even (Protected)
*857e15c0 size:   40 previous size:   40  (Allocated) *Even (Protected)
Pooltag Even : Event objects
 857e1600 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1640 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1680 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e16c0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1700 size:   40 previous size:   40  (Allocated)  Even (Protected)
 857e1740 size:   40 previous size:   40  (Allocated)  Even (Protected)

You can see that we have 17 x 0x40 space free, which is exactly 0x440, and that I didn't have to deal with the details. I can give any other size, e.g:

lkd> !py c:\users\csaby\desktop\spray_helper.py
Give me the size of the hole in hex: 260
Process: 8572bd40
Object location: 87b2fe00
Pool page 87b2fe00 region is Nonpaged pool
 87b2f000 size:   98 previous size:    0  (Allocated)  IoCo (Protected)
 87b2f098 size:   90 previous size:   98  (Free)       ....
 87b2f128 size:   98 previous size:   90  (Allocated)  IoCo (Protected)
 87b2f1c0 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f258 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f2f0 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f388 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f420 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f4b8 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f550 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f5e8 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f680 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f718 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f7b0 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f848 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f8e0 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2f978 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2fa10 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2faa8 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2fb40 size:   98 previous size:   98  (Free )  IoCo (Protected)
 87b2fbd8 size:   98 previous size:   98  (Free )  IoCo (Protected)
 87b2fc70 size:   98 previous size:   98  (Free )  IoCo (Protected)
 87b2fd08 size:   98 previous size:   98  (Free )  IoCo (Protected)
*87b2fda0 size:   98 previous size:   98  (Allocated) *IoCo (Protected)
Owning component : Unknown (update pooltag.txt)
 87b2fe38 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2fed0 size:   98 previous size:   98  (Allocated)  IoCo (Protected)
 87b2ff68 size:   98 previous size:   98  (Allocated)  IoCo (Protected)

As we see the spraying adapts to our needs. Note that different objects were used this time. If you test it many times, try to use a number which will result in different object allocation, so you get a cleaner output.

Another important thing to note is that the hole creation is not 100% reliable here, but I believe it's very close. What I do is the following: I spray the kernel with 100000 objects, and free up X in the middle. Very likely that those will be reserved next to each other, and give us the space we need when I free them, and for demonstrating the 'automation' this was the easiest. It could be more reliable if:
  1. I try to make multiple holes, with freeing up multiple X handlers, possibly next to each other
  2. There is a way to leak the address of the objects from the kernel and calculate if they are next to each other, and thus freeing up the space that way. This will be the most reliable method.
As I progress, I will implement these but for now the first method makes it.

And yes, I code in Python, and not Powershell, simply because I can't code in PS, but I fully agree with everyone who says that making this in PS would make much more sense.

Part 4 will come later as I will be busy in the next 2 weeks, possibly no time for this, but will catch up after.

No comments: