0% found this document useful (0 votes)
56 views68 pages

2021 Sthack Windows Lpe

This document discusses discovering and exploiting a kernel pool overflow on Windows 10. It begins with an introduction to the speaker and an overview of Pwn2Own 2021 where the speaker targeted a local privilege escalation on Windows 10. It then covers the Windows 10 kernel attack surface, focusing on drivers as a potential vulnerability source. Steps are described to identify writable drivers on the system and obtain information on a selected "Spaceport" driver to begin the vulnerability discovery process.

Uploaded by

煮米君
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
56 views68 pages

2021 Sthack Windows Lpe

This document discusses discovering and exploiting a kernel pool overflow on Windows 10. It begins with an introduction to the speaker and an overview of Pwn2Own 2021 where the speaker targeted a local privilege escalation on Windows 10. It then covers the Windows 10 kernel attack surface, focusing on drivers as a potential vulnerability source. Steps are described to identify writable drivers on the system and obtain information on a selected "Spaceport" driver to begin the vulnerability discovery process.

Uploaded by

煮米君
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 68

1

Discovering and exploiting


a kernel pool overflow on
modern Windows 10

Sthack 2021
~:$ whoami 2

 Fabien Perigaud
 @0xf4b on Twitter

 Working for Synacktiv


 Offensive security
 90 ninjas: pentest, reverse engineering, development
 We are hiring!

 Reverse engineering team technical leader


 30 reversers
 Reverse, vulnerability research and exploitation, low level dev

2 / 68
Agenda 3

 This short introduction


 Pwn2Own Vancouver 2021
 Windows 10 Kernel Attack Surface
 Vulnerability Discovery
 Exploitation: “expecting shell root”
 Results and Conclusion

3 / 68
4

Pwn2Own Vancouver 2021


Pwn2Own 5

 “Hacking contest” by ZDI


 Prove exploitation of devices or software in widespread use
 Usually takes place twice a year
 Vancouver: virtualization, browsers, OS, Tesla, ...
 Tokyo: smartphones, “smart devices”, routers, printers, …
 Miami 2020: ICS/SCADA
 Pwn all the things!
 Payload should prove arbitrary code execution
 Remote/elevated shell, blinking leds on a router, image display on a
printer…

5 / 68
Pwn2Own - Rules 6

 Each category has its own rules...


 … but rules can be adapted if kindly asked :)
 For example, enabling a non-default service…
 … as long as this configuration matches reality!
 5 minutes time slot, up to 3 attempts
 Exploit can be time consuming (race condition, huge allocations,
…)
 Stability can be perfectible :)

6 / 68
Pwn2Own – Rules (2) 7

 For each target, only the first pwn is considered a WIN


 In practice, every successful pwn is a WIN…
 … unless a vulnerability collision occurs…
 … between contestants, or with the vendor!

 Order of the contestants is drawn at random


 Small papers in a hat :)
 In the end, Synacktiv contestants usually get the last slot

7 / 68
Pwn2Own – Rewards 8

 Each pwned target is rewarded, depending on the estimated


difficulty
 Cash prize (way lower than the 0-day market, but still
interesting :))
 Master of Pwn points → additional cash prize for the “Master of
Pwn”
 Add-on bonus
 For some targets
 LPE, sandbox escape, …
 Usually a few more points and dollars

8 / 68
Pwn2Own Vancouver 2021 9

 Targets
 Desktop browsers (Chrome, Safari, Firefox, Edge)
 Enterprise applications (Office, Reader, Zoom, Teams)
 Server (RDP, Exchange, SharePoint)
 Automotive (Tesla Model 3)
 Local Privilege Escalation (Windows, Ubuntu)

 LPEs: “easier” targets!

9 / 68
Pwn2Own Vancouver 2021 – Windows 10 LPE 10

 Focus on the Windows 10 LPE

 Interesting execution context


 Unprivileged user…
 … but no sandboxing!
 Medium integrity level

 Vulnerability must be in the kernel

10 / 68
11

Windows 10 Kernel Attack Surface


Kernel Attack Surface 12

 Ntoskrnl
 Windows kernel image
 Interrupts, memory management, kernel objects (processes, threads, files,
registry, …), syscalls and more
 Very interesting target, might be reachable from the hardest sandbox level
 Drawback: huge focus from security researchers
 Win32k
 Huge graphic subsystem, own syscall table
 Old code base, many vulnerabilities
 Also reachable from some sandbox contexts
 Drawback: also a huge focus from security researchers

12 / 68
Kernel Attack Surface - Drivers 13

 Drivers
 PE loaded in Kernel-land
 “.sys” file on the disk
 Usually linked to a service

 Userland access
 Driver create a Device object “XXX”
 Userland opens the device through “\\?\GLOBALROOT\Device\
XXX”

13 / 68
Kernel Attack Surface - Devices 14

 List all the devices present on a default Windows 10


 WinDBG to the rescue!
 !object \Device → 159 devices
0: kd> !object \Device
Object: ffff988952031060 Type: (ffffaf07a6c71900) Directory
ObjectHeader: ffff988952031030 (new version)
HandleCount: 2 PointerCount: 65717
Directory Object: ffff988952041e60 Name: Device

Hash Address Type Name


---- ------- ---- ----
00 ffffaf07adaafcb0 Device 00000030
ffffaf07ad94a050 Device NDMP2
ffffaf07a82c8360 Device NTPNP_PCI0002
01 ffffaf07ad9d2050 Device NDMP3
...
14 / 68
Check devices access 15

 Dumb/dirty way to check access rights


 Try to open device with R/W access
 Fast to write with Python ctypes

for dev in devices:


file_handle = windll.kernel32.CreateFileA("\\\\?\\GLOBALROOT\\Device\\
%s" % dev, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0)
if file_handle != INVALID_HANDLE_VALUE:
print("[*] OK %s" % dev)

15 / 68
Check devices access (2) 16

C:\Users\unpriv\Desktop>check_devices.py
...
[*] OK Afd
[*] OK ahcache
[*] OK Beep
[*] OK CNG
[*] OK gpuenergydrv
[*] OK KsecDD
[*] OK LanmanDatagramReceiver
[*] OK Mailslot
[*] OK Mup
[*] OK NamedPipe
[...]
[*] OK Netbios
[...]
[*] OK Null
[*] OK PEAuth
[*] OK RdpBus
[*] OK Spaceport
[*] OK Tdx
[*] OK USBFDO-0
[*] OK USBPDO-0
[*] OK VBoxGuest
[*] OK WindowsTrustedRT
[*] OK WINDRVR6
16 / 68
Pick a victim 17

Afd ahcache Beep CNG gpuenergydrv

KsecDD LanmanDatagramReceiver Mailslot Mup NamedPipe

Netbios Null PEAuth RdpBus Spaceport

Tdx USBFDO-0 USBPDO-0 VBoxGuest

WinDRV6 WindowsTrustedRT

17 / 68
Pick a victim 18

Afd ahcache Beep CNG gpuenergydrv

KsecDD LanmanDatagramReceiver Mailslot Mup NamedPipe

Netbios Null PEAuth RdpBus Spaceport

Tdx USBFDO-0 USBPDO-0 VBoxGuest

WinDRV6 WindowsTrustedRT

18 / 68
Pick a victim 19

Afd ahcache Beep CNG gpuenergydrv

KsecDD LanmanDatagramReceiver Mailslot Mup NamedPipe

Netbios Null PEAuth RdpBus Spaceport

Tdx USBFDO-0 USBPDO-0 VBoxGuest

WinDRV6 WindowsTrustedRT

19 / 68
Pick a victim 20

Afd ahcache Beep CNG gpuenergydrv

KsecDD LanmanDatagramReceiver Mailslot Mup NamedPipe

Netbios Null PEAuth RdpBus Spaceport

Tdx USBFDO-0 USBPDO-0 VBoxGuest

WinDRV6 WindowsTrustedRT

20 / 68
Find the corresponding driver 21

 WinDBG again

0: kd> dt nt!_DEVICE_OBJECT ffffaf07a83320a0


+0x000 Type : 0n3
+0x002 Size : 0xc58
+0x004 ReferenceCount : 0n0
+0x008 DriverObject : 0xffffaf07`a8323cf0 _DRIVER_OBJECT
[...]
0: kd> !object 0xffffaf07`a8323cf0
Object: ffffaf07a8323cf0 Type: (ffffaf07a6cca380) Driver
ObjectHeader: ffffaf07a8323cc0 (new version)
HandleCount: 0 PointerCount: 5
Directory Object: ffff988952132220 Name: spaceport

21 / 68
Find the corresponding driver 22

 Corresponding service in the registry

22 / 68
23

Vulnerability Discovery
Driver interaction 24

 Driver Object can have 28 defined Major Functions


 Open / Close
 Read / Write
 IOCTL
 Etc.
 Usually defined in the DriverEntry function
 IOCTLs are usually the first thing to look at ...
 … but others are also less analyzed!

24 / 68
Driver interaction – Major Functions 25

25 / 68
Driver interaction – Major Functions 26

26 / 68
SpControlDeviceControl 27

 58 handled IOCTLs

 Manual review
 Focus on “SpIoctl[Create|Set].*”
 Ignore when privileges are checked (“SpAccessCheck.*”)

27 / 68
SpControlDeviceControl 28

 Many checks are missing


 When the research was performed :)

 Several vulnerabilities have been found!


 Constraints to be reachable
 Might require creation of objects from a privileged context
 Might be OK in real life, not for the contest

 One really interesting vulnerability

28 / 68
SpIoctlSetControlWork 29

 No permission check
 Buffer overflow 101
 Controlled memcpy size
 Controlled content

29 / 68
SpIoctlSetControlWork 30

 No permission check
 Buffer overflow 101
 Controlled memcpy size
 Controlled content

Dest:
pool (heap)
buffer

30 / 68
SpIoctlSetControlWork 31

 No permission check
 Buffer overflow 101
 Controlled memcpy size
 Controlled content

Source:
user controlled
buffer

31 / 68
SpIoctlSetControlWork 32

 No permission check
 Buffer overflow 101
 Controlled memcpy size
 Controlled content

Size:
from user
controlled
buffer
32 / 68
SpIoctlSetControlWork – Reaching the bug 33

 SpIoctlSetControlWork looks for a SP_WORK_INFO in a


doubly-linked list (“LIST1”)
 Identified by a provided ID
 “LIST1” is populated by another IOCTL: SpIoctlGetControlWork
 Gets an entry from another doubly-linked list (“LIST2”)
 Put it in “LIST1” and return its ID
 “LIST2” is populated by SP_CONTROL_WORK::Run
 Reachable from several IOCTLs
 SpIoctlAttachSpaceRemote is a good candidate

33 / 68
Workflow 34

 If we call the IOCTLs sequentially


 Stuck when calling SpIoctlGetControlWork → we never get an ID

 What’s happening?

34 / 68
Usual workflow 35

xxx.exe
Sp
Io
ct l
At
tac
hS
p ac
eR
em
ot
e

spaceport.sys

35 / 68
Usual workflow 36

xxx.exe spaceman.exe
Sp
Io
ct l

)
NF
At
tac

(W
hS

ns
p

aw
ac
eR

Sp
em
ot
e

spaceport.sys

36 / 68
Usual workflow 37

xxx.exe spaceman.exe
Sp

SpIoctlGetControlWork
Io
ct l

)
NF
At
tac

(W
hS

ns
p

aw
ac
eR

Sp
em
ot
e

spaceport.sys

37 / 68
38 / 68
38

SpIoctlSetControlWork
spaceman.exe
Usual workflow

SpIoctlGetControlWork
spaceport.sys
)
NF
(W
ns
aw
Sp
e
ot
em
eR
p ac
hS
tac
xxx.exe
At
ct l
Io
Sp
Usual Workflow 39

 If we call the IOCTLs sequentially, we get raced by spaceman.exe


 No more ID to be retrieved by SpIoctlGetControlWork

 However…
 … meet asynchronous DeviceIoControl!
 Call SpIoctlGetControlWork before SpIoctlAttachSpaceRemote
 List is empty, driver puts the request on hold
 IRP is queued, and dequeued when SP_CONTROL_WORK::Run is
executed
 When spaceman.exe is executed, it has been raced by us :)

39 / 68
PoC 40

 Thread #1 → call SpIoctlGetControlWork


 Async, wait for result
 Thread #2 → call SpIoctlAttachRemoteSpace
 Blocked until someone issues a SpIoctlSetControlWork
 Thread #1 → ID retrieved, call SpIoctlSetControlWork with
bogus length

40 / 68
PoC - BSOD 41

rax=ffffaf07b1584ed0 rbx=0000000000000000 rcx=ffffaf07f39a9112


rdx=fffffffffa81d190 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80465dfa343 rsp=ffffc106ae2686d8 rbp=fffff80465e32048
r8=0000000042424242 r9=ffffaf07a83321f0 r10=0000000000000000
r11=ffffaf07ee1c62a2 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei ng nz na po nc

spaceport!memcpy+0x203:
fffff804`65dfa343 0f104411f0 movups xmm0,xmmword ptr [rcx+rdx-10h]
ds:ffffaf07`ee1c6292=????????????????????????????????

ffffc106`ae2686d8 fffff804`65df6f19 : [...] : spaceport!memcpy+0x203


ffffc106`ae2686e0 fffff804`65e4c12b : [...] : spaceport!SP_CONTROL_WORK::Set+0xd9
ffffc106`ae268740 fffff804`65e41d7e : [...] : spaceport!SpIoctlSetControlWork+0x5b
ffffc106`ae268780 fffff804`65df3f50 : [...] : spaceport!SpControlDeviceControl+0x2ee

41 / 68
42

Exploitation: “expecting shell root”


Vulnerability primitive 43

 Pool overflow
 The pool is the Windows Kernel heap

 Target allocation
 SP_WORK_INFO allocation is made in the NonPagedPoolNx
 Size is 0x160 bytes
 Lies in the LFH (Low Fragmentation Heap)

 Overflow constraints
 None :)
 We control content and size

43 / 68
Mitigations 44

 kASLR
 Not a problem, Medium integrity level
 Various kernel APIs to get objects and modules addresses

 DEP / SMEP / CFG


 Kernel code execution is hard
 Data-only exploitation ftw!

 SMAP?
 Only in a few contexts, not in ours :)

44 / 68
Exploitation strategy 45

 Data only
 We want to run an elevated cmd.exe

 Target: process Token!


 Swap token with a privileged one? (System)
 Or enable powerful privileges!

 Let’s turn our pool overflow into something interesting!

45 / 68
La French Tech – SSTIC 2020 46

46 / 68
La French Tech – SSTIC 2020 47

 Aligned Chunk Confusion


 New generic pool overflow exploitation method
 Abuses the CacheAligned bit in the POOL_HEADER
 Read their paper for details!

 Requirements
 Shape the pool to control the chunk after the vulnerable one

47 / 68
Pool massaging 48

 Spray a bunch of 0x160 bytes allocations in NonPagedPoolNx


 We can use pipe objects
 PipeQueueEntry is in the NonPagedPoolNx, and we can control its
size
QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry

48 / 68
Pool massaging 49

 Spray a bunch of 0x160 bytes allocations in NonPagedPoolNx


 We can use pipe objects
 PipeQueueEntry is in the NonPagedPoolNx, and we can control its
size
QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry

 Free a few ones to create holes


QueueEntry Free QueueEntry QueueEntry QueueEntry QueueEntry Free QueueEntry QueueEntry

49 / 68
Pool massaging 50

 Spray a bunch of 0x160 bytes allocations in NonPagedPoolNx


 We can use pipe objects
 PipeQueueEntry is in the NonPagedPoolNx, and we can control its
size
QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry QueueEntry

 Free a few ones to create holes


QueueEntry Free QueueEntry QueueEntry QueueEntry QueueEntry Free QueueEntry QueueEntry

 Our vuln chunk should lie in one of these holes


QueueEntry Free QueueEntry QueueEntry QueueEntry QueueEntry Vuln Chunk QueueEntry QueueEntry

50 / 68
Creating an overlapping chunk 51

 We can now trigger the overflow to change the next chunk


(called victim) POOL_HEADER
 poolType set to 4 → CacheAligned
 previousSize set to 0x100
 When freeing the victim, the allocator will look for a second
POOL_HEADER 0x100 bytes before the chunk
 This creates a fake chunk of 0x260 bytes (0x160+0x100)
 Caveat: after exploitation, our vuln chunk is freed
 We reuse it with a controlled PipeQueueEntry!

51 / 68
Creating an overlapping chunk - Graphics 52

Pool Pool
Header
Vuln Chunk Header
PipeQueueEntry

Pool Pool
Header
Vuln Chunk Header
PipeQueueEntry

Overflow

New
Pool
Header
Free Chunk Pool PipeQueueEntry
Header

Fake New
Pool
Header
New PipeQueueEntry Pool Pool PipeQueueEntry
Header Header

52 / 68
Creating an overlapping chunk – Next steps 53

 We chose a size of 0x260 bytes for our fake chunk


 LFH is for allocations < 0x200
 Bigger allocations are handled by the VS (variable size) allocator
 Lookaside lists can be enabled for faster allocations!

 When the victim chunk is freed


 Fake POOL_HEADER is read
 Fake chunk is added to the 0x260 bytes lookaside list

 We can now make a new allocation of 0x260 bytes to reuse the fake
chunk!

53 / 68
Creating an overlapping chunk – Result 54

Pool
Header
PipeQueueEntry

Pool
Header
PipeQueueEntry

Overlapping chunks

 Leak by reading the first pipe


 Gives the PipeQueueEntry structure content

54 / 68
PipeQueueEntry structure 55

List of PipeQueueEntry structures


struct PipeQueueEntry {
LIST_ENTRY list;
IRP *IRP;
uint64_t security;
int isDataInKernel;
int remaining_bytes;
int DataSize;
int field_2C;
char data[0]; if (PipeQueueEntry->isDataInKernel == 1)
data_ptr = (PipeQueueEntry->linkedIRP->SystemBuffer);
}; else
data_ptr = PipeQueueEntry->data;

55 / 68
Arbitrary Read Primitive 56

 Free the first PipeQueueEntry


 Reuse the chunk
 We can overwrite the second PipeQueueEntry structure
 Change the linkedIRP pointer to make it point to userland
IRP

SystemBuffer

Userland
Kernel
PipeQueueEntry

Useful Kernel Data


*linkedIRP
isDataInKernel = 1

56 / 68
Attacking the ProcessBilled pointer 57

 POOL_HEADER has a ProcessBilled field


 Obfuscated pointer to an EPROCESS
 If PoolQuota flag is set, EPROCESS→QuotaBlockPtr→value is
decremented when allocation is freed

 Arbitrary decrement primitive


 Requires ability to forge a new obfuscated pointer
 ProcessBilled == @EPROCESS ^ @chunk ^ ExpPoolQuotaCookie

57 / 68
Finding ExpPoolQuotaCookie 58

 From Medium Integrity Level, kernel APIs can be used to get a


kernel address from a handle
 NtQuerySystemInformation(SystemHandleInformation)
 We have our EPROCESS address!

 In the previous leak


 We got our PipeQueueEntry address through the doubly-linked list
 We got its POOL_HEADER ProcessBilled

 ExpPoolQuotaCookie can be computed

58 / 68
Arbitrary decrement to privilege escalation 59

 A process Token contains its privileges


 Strategy: decrement “Enabled” and “Present” fields to enable
SeDebugPrivilege
 We build a fake EPROCESS which QuotaBlockPtr points to the
target field
 2 more reuses needed to change the ProcessBilled twice
Fake EPROCESS
ProcessToken
POOL_HEADER QuotaBlockPtr
PoolType = PoolQuota [1] Privileges.Present
Tag Privileges.Enabled
Fake EPROCESS
ProcessBilled =
PEPROCESS ^ Cookie ^ Address [2] QuotaBlockPtr

59 / 68
SeDebugPrivileges 60

 Allow debugging every process on the system

 Strategy
 Open winlogon.exe
 Inject shellcode
 Spawn a SYSTEM cmd.exe

 Quick demo!

60 / 68
61

Results and Conclusion


Pwn2Own results 62

 Exploit worked at first attempt \o/

 Debriefing with ZDI


 Bug is unknown to ZDI \o/ \o/
 Debriefing with Microsoft
 … bug is already known to Microsoft …
 They proved it by showing the bug report

 Partial Win :(

62 / 68
Timeline 63

 ?? ?? ???? - Vulnerability reported to Microsoft by “vbty”


(according to Microsoft advisory)
 8 April 2021 – Vulnerability exploited during P2O
 13 July 2021 – Vulnerability fixed by Microsoft (CVE-2021-
33751)

63 / 68
Fix 64

64 / 68
Free 0day? 65

65 / 68
Additional fix... 66

66 / 68
Final words 67

 A generic pool overflow exploitation method exists!


 … and works on real cases!
 Thanks to the new kernel pool from 19H1

 Try Pwn2Own!
 Some targets do not require so much effort
 Attack surface is quite huge!

67 / 68
68

https://www.linkedin.com/company/synacktiv
https://twitter.com/synacktiv
Nos publications sur : https://synacktiv.com

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy