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.

No comments: