Windows 10 Pool Party

1.1

BAYET Corentin

2017

Windows 10 Pool Party

  • Exploitation in the NonPagedPool
  • Exploitation at medium integrity level
  • Attacking drivers and IOCTLs
  • Tools and methods to attack pool

What we WILL talk about

What we WONT talk about

  • Win32k.sys, GDI / USER objects
  • Exploitation at low integrity level

1.2

First crash

2.1

What is the kernel pool ?

  • Place for every allocation in the windows kernel

  • Common for every drivers

  • Specific allocator and structures

  • Several types:

    • NonPagedPool

    • PagedPool

    • ....

Basically, a list of pages fragmented in chunks !

2.2

A pool chunk

2.3

First crash

IOCTL: Input/Ouput Control

BOOL WINAPI DeviceIoControl(
  _In_        HANDLE       hDevice,
  _In_        DWORD        dwIoControlCode,
  _In_opt_    LPVOID       lpInBuffer,
  _In_        DWORD        nInBufferSize,
  _Out_opt_   LPVOID       lpOutBuffer,
  _In_        DWORD        nOutBufferSize,
  _Out_opt_   LPDWORD      lpBytesReturned,
  _Inout_opt_ LPOVERLAPPED lpOverlapped
);

I/O Control Code

2.4

First crash

METHOD_BUFFERED:

  1. The I/O Manager allocates a buffer in the NonPaged Pool with the biggest size provided: it's the SystemBuffer
  2. The I/O Manager copies the InputBuffer in the SystemBuffer and pass it to the driver
  3. The driver handles the IOCTL, and writes the return in the SystemBuffer by overwriting the input. The driver must also tell to the I/O Manager how much he has written.
  4. The I/O Manager copies the content of the SystemBuffer in the OutputBuffer using the size provided by the driver.

So we control the size of the buffer used for input and ouput in drivers... Great Attack Vector !

2.5

The vulnerability

About CVE-2017-6008

A memcpy is called with following arguments:

  • Dest: The SystemBuffer (we control the size)
  • Src: A full controlled buffer (from our Input Buffer)
  • Size: the size of src

Classic Buffer Overflow... But in the NonPagedPool  !

2.6

Pool History

  • Deobfuscate Pool Internals
  • Presents severals generic attacks

2.7

Points to data controlled by attacker

  • Using a pool buffer overflow to overwrite Process pointer
  • Craft a fake EPROCESS structure
  • Triggers an arbitrary decrementation when the overflowed chunk is free

Quota Process Pointer Overflow

2.8

DEMO

History of the Pool

  • REAL safe linking/unlinking
  • Pool Index validation
  • SMEP
  • MIN_MAP_ADDR (reverted on windows 7 and vista x64)
  • NonPagedPoolNx (DEP)

Windows 8 Introduced a lot of mitigations:

About the attack we used:

  • Process Billed encoded with a cookie
  • The free algorithms checks if the pointer is in kernel-land

3.1

Nowadays Pool Chunk

Process Billed encoded:

PoolCookie XOR Chunk Address XOR Pointer

Checked before use

3.2

Today

  • Exploiting vulnerabilities in the Pool is pretty hard
  • No generic attacks

Goal: exploit the very same pool buffer overflow on Windows 10

3.3

What do we need

Quota Process Pointer Overwrite:

  • The Pool Cookie
  • The address of the overflowed chunk
  • Arbitrary data in kernel-land at known address

Seems impossible...

3.4

Pool Spraying

  • Spraying is the art of making the further allocations predictible using the allocator behavior
  • Provides you knowledge and control

4.1

Allocator Behavior

  • Lookaside list (for chunks with a size <= 0x200)
  • ListHeads list

Two lists of free chunks :

4.2

Lookaside List

  • Contains chunk with a size ≤ 0x200 bytes
  • Can contains only 255 chunks of the same size

4.3

Allocator behavior

Allocation algorithm

4.4

Allocator behavior

Allocation of a new page

4.5

Allocator behavior

Free algorithm

4.6

Windows API tools

  • A lot of different objects:
    • Reserved Objects
    • Semaphores
    • Processes
    • Register keys
    • Files
  • With various size
  • Allocated in differents pools (Paged, NonPaged...)

Windows named objects :

5.1

Windows API tools

In userland, use a handle to interact with the object !

5.2

Basic Pool Spraying

Step 1: Derandomize the pool

  • Empty the Lookaside List
  • Empty the ListHead List
  • Create pages filled of our object

AKA : Massively allocate chunks

6.1

Basic Pool Spraying

User-land

Kernel-land

Step 2: Create Gaps

CloseHandle()

Chunks are freed and coalesced

6.2

Basic Pool Spraying

Problems:

  • We can't predict allocations with a size <= 0x200 bytes
    • Or we need an object with the exact same size of the gap we want...
  • Even if it's very likely, we're not sure the gaps we created actually exists
  • We don't know the kernel addresses of our gaps

We can fix this

6.3

Another windows tool

NtQuerySystemInformation

SystemExtendedHandleInformation

Retrieve any object's kernel address using its handle

Well known leak

6.4

Advanced Pool Spraying

Step 1 : Derandomize the Pool

Step 2 : Find the perfect gap

7.1

Leak addresses

Check if offsets are correct

Step 2 : Find the perfect gap

Advanced Pool Spraying

7.2

Step 3 : Enjoy your gaps !

Advanced Pool Spraying

  • We can predict a future allocation at 100%
  • And we know its kernel address
  • Just Windows, only Windows

Time to start having fun !

7.3

What do we need

Quota Process Pointer Overwrite:

  • The Pool Cookie
  • The address of the overflowed chunk
  • Arbitrary data in kernel-land at known address

8.1

Arbitrary data in kernel-land at known address

CreatePrivateNamespace() Function:

In paged pool, in the chunk of the object allocated

8.2

Arbitrary data in kernel-land at known address

8.3

What do we need

Quota Process Pointer Overwrite:

  • The Pool Cookie
  • The address of the overflowed chunk
  • Arbitrary data in kernel-land

8.4

The Pool Cookie

  • Symbol: nt!ExpPoolQuotaCookie
  • Generated at boot
  • Good enthropy

8.5

Process Billed encoded:

PoolCookie XOR Chunk Address XOR Pointer

The Pool Cookie

Allocated chunk

Free chunk

Process Billed encoded:

PoolCookie XOR Chunk Address

8.6

The Pool Cookie

  1. Spray the pool in order to have controllable chunks
  2. Free a chunk
  3. Free the chunk just before
  4. Reallocate a chunk with the size of the gap
  5. The data is not overwritten... With a correct IOCTL, you might be able to read the old headers... containing the PoolCookie XORED with old chunk address

8.7

About CVE-2017-7441

The Pool Cookie

  • Use our input to call the function RtlLookupElementGenericTableAvl
  • Write the result in the SystemBuffer for return but doesn't wipe the whole buffer
  • Because of unicode and bad calculation, specify a wrong number to the IOManager: the driver write n bytes and tell n+2 to the driver
  • 2 bytes Out-Of-Bounds read
  • It's enough to leak the PoolCookie !

8.8

What do we need

Quota Process Pointer Overwrite:

  • The Pool Cookie
  • The address of the overflowed chunk
  • Arbitrary data in kernel-land at known address

Let's exploit !

9.1

DEMO

Conclusion

Drivers are still a great attack vector:

  • A buffer is used for input/output and we control its size...
  • A buffer overflow is exploitable !

Be careful when writing a driver...

  • You're dealing with user input in kernel land...
  • The tyniest mistake becomes a critical vulnerability

Completely remediate the NtQuerySystemInformation leak !

10.1

QUESTIONS ?

10.2

Thanks for listening !

I'm interested in job offers !

deck

By theduck

deck

  • 764
Loading comments...

More from theduck