Ncc Group Plc · LSE:NCC

Ncc — Investor Presentation

70 slides from Ncc's investor presentation

  1. Slide 1: Pwning the Windows 10 Kernel with NTFS and WNF - POC 2021

    Pwning the Windows 10 Kernel with NTFS and WNF - POC 2021

  2. Slide 2: Introduction

    Introduction

  3. Slide 3: About

    About Currently a Security Researcher within NCC Exploit Development Group (EDG). Supported by other team members at NCC (Cedric Halbronn and Aaron Adams). Previously won some Pwn2Own's (2018 Apple Safari / 2017 Huawei Mate Pro etc) Research interests primarily platform security (OS's/Mobile/Browser

  4. Slide 4: Background

    Background A Windows local kernel priv escalation CVE-2021-31956 vulnerability affecting a large range of versions Based on a vulnerability found exploited in the wild by Boris Larin of Kaspersky Challenges around exploit development on latest Windows 10 version at the time - 20H2 (Segment Heap etc)

  5. Slide 5: Agenda

    Agenda Vulnerability Overview WNF Introduction WNF Exploit Primitives Exploitation without CVE-2021-31955 Post Exploitation Reliability and Cleanup Exploit Testing Detection 5 / 70

  6. Slide 6: Vulnerability Overview

    Vulnerability Overview NTFS Vulnerability Details

  7. Slide 7: Vulnerability Details

    Vulnerability Details To exploit a vulnerability we first need a good understanding of the issue. Kaspersky had done a lot of the initial triage in their blog. However, from an exploit developer perspective, we need to understand all the contraints and flexibility it offers. In this case "is it a go

  8. Slide 8: Vulnerability Details

    Vulnerability Details __int64 __fastcall NtfsQueryEaUserEaList(__int64 a1, __int64 eas_blocks_for_file, __int64 a3, __int64 out_buf, unsigned int out_buf_length, unsigned int *a6, char a7) { unsigned int padding; // er15 padding = 0; for ( i = a6; ; i = (unsigned int *)((char *)i + *i) ) { if ( i ==

  9. Slide 9: Vulnerability Details

    Vulnerability Details *((_BYTE *)out_buf_pos + *((unsigned __int8 *)v11 + 4) + 8) = 0; LABEL_8: v18 = ea_block_size + padding + v15; occupied_length = v18; if ( !a7 ) { if ( v23 ) *v23 = (_DWORD)out_buf_pos - (_DWORD)v23; if ( *v11 ) { v23 = out_buf_pos; out_buf_length -= ea_block_size + padding; pa

  10. Slide 10: Vulnerability Details

    Vulnerability Details Lets put some sample numbers into this. Assume two EA's so two iterations of the loop. First iteration: EaNameLength = 5 EaValueLength = 4 ea_block_size = 9 + 5 + 4 = 18 padding = 0 So assuming that 18 < out_buf_length - 0, data would be copied into the buffer. We will use 30 f

  11. Slide 11: Vulnerability Details

    Vulnerability Details Asssume second extended attribute with the same values EaNameLength = 5 EaValueLength = 4 ea_block_size = 9 + 5 + 4 = 18 At this point padding is 2, so the calculation is: 18 <= 12 - 2 // is False. Second memcpy fails as it would overflow the buffer. 11 / 70

  12. Slide 12: Vulnerability Details

    Vulnerability Details So lets consider an overflowing case (when output buffer size is 18). First EA: EaNameLength = 5 EaValueLength = 4 Second Ea: EaNameLength = 5 EaValueLength = 47 First iteration the loop: EaNameLength = 5 EaValueLength = 4 ea_block_size = 9 + 5 + 4 // 18 padding = 0 // First ti

  13. Slide 13: Vulnerability Details

    Vulnerability Details 18 <= 18 - 0 // is True and a copy of 18 occurs. out_buf_length = 18 - 18 + 0 out_buf_length = 0 // out_buf_len has been decremented (0 bytes left). padding = ((18+3) & 0xFFFFFFFC) - 18 padding = 2 Second iteration of loop: EaNameLength = 5 EaValueLength = 47 ea_block_size = 5

  14. Slide 14: Vulnerability Details

    Vulnerability Details Next Questions are: Where is the buffer allocated? Can we control the contents of the overflow? The allocation NtfsCommonQueryEa: if ( (_DWORD)out_buf_length ) { out_buf = (PVOID)NtfsMapUserBuffer(a2, 16i64); v28 = out_buf; v16 = (unsigned int)out_buf_length; if ( *(_BYTE *)(a2

  15. Slide 15: Triggering the Corruption

    Triggering the Corruption To anwser the second question we need to look at how to trigger the overflow. Looking at the callers for NtfsCommonQueryEa we can see NtQueryEaFile as NT syscall. NTSTATUS NtQueryEaFile( HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, BOOLEAN

  16. Slide 16: Triggering the Corruption

    Triggering the Corruption NtSetEaFile is the way to set extended attributes NTSTATUS ZwSetEaFile( HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length ); Key thing here is Buffer which needs to be crafted correctly. 16 / 70

  17. Slide 17: Triggering the Corruption

    Triggering the Corruption Buffer is a pointer to a caller-supplied, FILE_FULL_EA_INFORMATION structured input buffer that contains the extended attribute values to be set. typedef struct _FILE_FULL_EA_INFORMATION { ULONG NextEntryOffset; UCHAR Flags; UCHAR EaNameLength; USHORT EaValueLength; CHAR Ea

  18. Slide 18: Vulnerability Summary

    Vulnerability Summary 1. The attacker can control the data which is used within the overflow and the size of the overflow. Extended attribute values do not constrain the values which they can contain. 2. The overflow is linear and will corrupt any adjacent pool chunks. 3. The attacker has control ov

  19. Slide 19: Windows 10 Kernel Pool Layout

    Windows 10 Kernel Pool Layout What does the Kernel Memory look like? Aim to cover some of the basics here Recommend reading the following papers: Scoop the Windows 10 Pool by by Corentin Bayet and Paul Fariello Windows Kernel Heap by scwuaptx Windows Heap Backed Pool by Yarden Shafir 19 / 70

  20. Slide 20: Windows 10 Kernel Pool Layout

    Windows 10 Kernel Pool Layout Allocator Backends: Low Fragmentation Heap (LFH) Variable Size Heap (VS) Segment Allocation Large Alloc In this talk we are going to focus on exploitation on the LFH. 20 / 70

  21. Slide 21: Windows 10 Kernel Pool Layout

    Windows 10 Kernel Pool Layout When I started doing this research I actually imposed more constraints that needed on myself. Going to talk about exploitation this way, then and improved iteration of the exploit. Pool page ffff9a069986f3b0 region is Paged pool ffff9a069986f010 size: 30 previous size:

  22. Slide 22: Windows 10 Kernel Pool Layout

    Windows 10 Kernel Pool Layout !pool @r9 ffff8001668c4d80 size: 30 previous size: 0 (Allocated) *NtFE Pooltag NtFE : Ea.c, Binary : ntfs.sys ffff8001668c4db0 size: 30 previous size: 0 (Free) C... 1: kd> dt !_POOL_HEADER ffff8001668c4d80 nt!_POOL_HEADER +0x000 PreviousSize : 0y00000000 (0) +0x000 Pool

  23. Slide 23: WNF Introduction

    WNF Introduction Windows Notification Framework

  24. Slide 24: WNF Introduction

    WNF Introduction The original Kaspersky article mentioned the in-the-wild attackers were using WNF. This was a novel exploitation techique to enable arbitrary r/w. WNF is an undocumented subsystem of the Windows Kernel. However, there has been previous research from a how it works and logic bugs per

  25. Slide 25: WNF Exploit Primitives

    WNF Exploit Primitives Windows Notification Framework Primitives

  26. Slide 26: Controlled Page Pool Allocations

    Controlled Page Pool Allocations Key observation here, that WNF allocations are made within the Paged Pool (same as the NTFS overflowing chunk) The data used for notifications looks like this (header followed by the data itself): nt!_WNF_STATE_DATA +0x000 Header : _WNF_NODE_HEADER +0x004 AllocatedSi

  27. Slide 27: Controlled Page Pool Allocations

    Controlled Page Pool Allocations NtUpdateWnfStateData calls ExpWnfWriteStateData which has the following code: v19 = ExAllocatePoolWithQuotaTag((POOL_TYPE)9, (unsigned int)(v6 + 16), 0x20666E57u); Looking at the function prototype: extern "C" NTSTATUS NTAPI NtUpdateWnfStateData( _In_ PWNF_STATE_NAME

  28. Slide 28: Controlled Free

    Controlled Free Initial Setup ringO - paged pool Controlled Free _WNF_STATE_DATA ringO - paged pool ———— ncecgroup~

  29. Slide 29: Relative Memory Read

    Relative Memory Read Overflow into DataSize to corrupt the value and enable a larger memory read. nt!_WNF_STATE_DATA +0x000 Header : _WNF_NODE_HEADER +0x004 AllocatedSize : Uint4B +0x008 DataSize : Uint4B +0x00c ChangeStamp : Uint4B Read the data back using NtQueryWnfStateData 29 / 70

  30. Slide 30: Relative Memory Write

    Relative Memory Write Corrupt the AllocatedSize nt!_WNF_STATE_DATA +0x000 Header : _WNF_NODE_HEADER +0x004 AllocatedSize : Uint4B +0x008 DataSize : Uint4B +0x00c ChangeStamp : Uint4B extern "C" NTSTATUS NTAPI NtUpdateWnfStateData( _In_ PWNF_STATE_NAME StateName, _In_reads_bytes_opt_(Length) const VO

  31. Slide 31: Arbitrary Read (Pipe Attributes Technique)

    Arbitrary Read (Pipe Attributes Technique) Discussed within the Scoop the Windows 10 Pool paper. Relies in being able to overflow into an adjacent Pipe Attribute (also allocated on Paged Pool) Corrupt the list FLINK pointer and inject in fake "Pipe Attribute". 31 / 70

  32. Slide 32: Arbitrary Write (StateData Pointer Corruption)

    Arbitrary Write (StateData Pointer Corruption) Investigate if it is possible to corrupt the StateData pointer of a _WNF_NAME_INSTANCE to change relative write to arbitrary write. Fake sane values for DataSize and AllocatedSize Use ExpWnfWriteStateData to write controlled data to a controlled locatio

  33. Slide 33: StateName Lookup

    StateName Lookup So this works to overflow the StateData pointer. The aim was to point StateData at the leaked EPROCESS address from CVE-2021-31955. However in the process we destroy other fields within the struct: 1: kd> dt _WNF_NAME_INSTANCE ffffdd09b35c8310+0x10 nt!_WNF_NAME_INSTANCE +0x000 Heade

  34. Slide 34: StateName Lookup

    StateName Lookup How do we workaround this? StateName is used for the lookup. There is the external version of the StateName which is the internal version of the StateName XOR’d with 0x41C64E6DA3BC0074. For example, the external StateName value 0x41c64e6da36d9945 would become the following internall

  35. Slide 35: StateName Lookup

    StateName Lookup struct _WNF_SCOPE_INSTANCE { struct _WNF_NODE_HEADER Header; //0x0 struct _EX_RUNDOWN_REF RunRef; //0x8 enum _WNF_DATA_SCOPE DataScope; //0x10 ULONG InstanceIdSize; //0x14 VOID* InstanceIdData; //0x18 struct _LIST_ENTRY ResolverListEntry; //0x20 struct _WNF_LOCK NameSetLock; //0x30

  36. Slide 36: StateName Forgery

    StateName Forgery We dont know what element is going to be corrupted. However, with the control over the heap we can forge this. This is not very reliable though. 36 / 70

  37. Slide 37: Security Descriptor

    Security Descriptor The final thing we need to forge is the security descriptor Can point this to forged one within userspace. 1: kd> dx -id 0,0,ffffce86a715f300 -r1 ((ntkrnlmp!_SECURITY_DESCRIPTOR *)0xffff9e8253eca5a0) ((ntkrnlmp!_SECURITY_DESCRIPTOR *)0xffff9e8253eca5a0) : 0xffff9e8253eca5a0 [Type

  38. Slide 38: CVE-2021-31955 Information Leak

    CVE-2021-31955 Information Leak A seporate information leak vulnerability. Allows leaking the EPROCESS address from every process out. NtQuerySystemInformation with SUPERFETCH_INFORMATION discloses it. NtQuerySystemInformation only available at medium integrity. There's public POCs online for this n

  39. Slide 39: EPROCESS Overwrite

    EPROCESS Overwrite But the exploit is not very reliable.. can we improve this? 39 / 70

  40. Slide 40: Exploitation without CVE-2021-31955

    Exploitation without CVE-2021-31955

  41. Slide 41: Exploit Version 2

    Exploit Version 2 Aim was to exploit without using CVE-2021-31955 information leak. To allow exploitation from low integrity. To increase reliability to a high standard. More investigation of _WNF_NAME_INSTANCE Credits also to Yan ZiShuang who also published on this. 41 / 70

  42. Slide 42: _WNF_NAME_INSTANCE EPROCESS

    _WNF_NAME_INSTANCE EPROCESS nt!_WNF_NAME_INSTANCE +0x000 Header : _WNF_NODE_HEADER +0x008 RunRef : _EX_RUNDOWN_REF +0x010 TreeLinks : _RTL_BALANCED_NODE +0x028 StateName : _WNF_STATE_NAME_STRUCT +0x030 ScopeInstance : Ptr64 _WNF_SCOPE_INSTANCE +0x038 StateNameInfo : _WNF_STATE_NAME_REGISTRATION +0x0

  43. Slide 43: Goal Layout

    Goal Layout Ideal Layout WNF Chunk WNF Chunk NTFS Chunk = WNF_NAME_INSTANCE CreatorProcess’ LS Relative OOB Read StateData~ ringO - paged pool ncecgroup~

  44. Slide 44: LFH Randomisation

    LFH Randomisation LFH Allocation LFH Segment nccgroup”

  45. Slide 45: Spray and Overflow (Take 2)

    Spray and Overflow (Take 2) _WNF_NAME_INSTANCE is 0xA8 + the POOL_HEADER (0x10), so 0xB8 (Chunk size 0xC0) _WNF_STATE_DATA objects of size 0xA0 (which when added with the header 0x10 + the POOL_HEADER (0x10) we also end up with a chunk allocated of 0xC0. Possible corrupted data nt!_WNF_NAME_INSTANCE

  46. Slide 46: Problems and Solutions

    Problems and Solutions Only want to corrupt _WNF_STATE_DATA objects first but pool segment also contains _WNF_NAME_INSTANCE due to being the same size. Use only a 0x10 data size overflow and clean up afterwards. Unbounded _WNF_STATA_DATA could be positioned at end of chunk. NtQueryWnfStateData read

  47. Slide 47: Locating a _WNF_NAME_INSTANCE and overwriting the State

    Locating a _WNF_NAME_INSTANCE and overwriting the State At this point _WNF_STATE_DATA has been overflowed and unbounded the DataSize and AllocatedSize But how to we locate a _WNF_NAME_INSTANCE? Each has a byte patern "\x03\x09\xa8" in its header. Therefore from this we know the start and can work ou

  48. Slide 48: PreviousMode

    PreviousMode "When a user-mode application calls the Nt or Zw version of a native system services routine, the system call mechanism traps the calling thread to kernel mode. To indicate that the parameter values originated in user mode, the trap handler for the system call sets the PreviousMode fiel

  49. Slide 49: Locating PreviousMode from EPROCESS

    Locating PreviousMode from EPROCESS 49 / 70

  50. Slide 50: Stage 2 Diagram

    Stage 2 Diagram ringO - paged pool Accgroup* ..

  51. Slide 51: Abusing PreviousMode

    Abusing PreviousMode PreviousMode Overwrite

  52. Slide 52: Abusing PreviousMode

    Abusing PreviousMode Once we have set the StateData pointer of the _WNF_NAME_INSTANCE prior to the _KPROCESS ThreadListHead Flink we can leak out the value by confusing it with the DataSize and the ChangeTimestamp, we can then calculate the FLINK as FLINK = (uintptr_t)ChangeTimestamp << 32 | DataSiz

  53. Slide 53: Stage 3

    Stage 3 PreviousMode StateData* x ring - paged pool Relatve OOE Read Wie nccgroup® ..

  54. Slide 54: Game Over

    Game Over After setting PreviousMode to 0, arbitrary read/write across whole memory space using NtWriteVirtualMemory and NtReadVirtualMemory. Trivial to either: Walk the ActiveProcessLinks within the EPROCESS, obtain a pointer to a SYSTEM token and replace current token. Overwrite _SEP_TOKEN_PRIVILE

  55. Slide 55: Reliability and Testing

    Reliability and Testing Reliability and Testing

  56. Slide 56: Reliability

    Reliability At this point exploit is succesful! However, kernel memory can be in a bad state.. Can lead to a BSOD quicky after. Need to clean up kernel memory to maintain stability. There's a limit to what we can actually do though. 56 / 70

  57. Slide 57: PreviousMode Restoration

    PreviousMode Restoration Simply set PreviousMode back to 1 using NtWriteVirtualMemory If we don't do this we get a crash as follows: Access violation - code c0000005 (!!! second chance !!!) nt!PspLocateInPEManifest+0xa9: fffff804`502f1bb5 0fba68080d bts dword ptr [rax+8],0Dh 0: kd> kv # Child-SP Ret

  58. Slide 58: StateData Pointer Restoration

    StateData Pointer Restoration This one is more tricky. StateData pointer is free'd on process termination (i.e. Neds to be valid allocated address) Walk the Name Instance Tree and fix up 58 / 70

  59. Slide 59: StateData Pointer Restoration

    StateData Pointer Restoration QWORD* FindStateName(unsigned __int64 StateName) { QWORD* i; // _WNF_SCOPE_INSTANCE+0x38 (NameSet) for (i = (QWORD*)read64((char*)BackupScopeInstance+0x38); ; i = (QWORD*)read64((char*)i + 0x8)) { while (1) { if (!i) return 0; // StateName is 0x18 after the TreeLinks FL

  60. Slide 60: RunRef Restoration

    RunRef Restoration RunRef from _WNF_NAME_INSTANCE‘s in the process of obtaining our unbounded _WNF_STATE_DATA ExReleaseRundownProtection causes a crash because its been corrupted. Need to obtain a full list of _WNF_NAME_INSTANCES _EPROCESS WnfContext nt!_WNF_PROCESS_CONTEXT +0x000 Header : _WNF_NODE

  61. Slide 61: RunRef Restoration

    RunRef Restoration void FindCorruptedRunRefs(LPVOID wnf_process_context_ptr) { // +0x040 TemporaryNamesListHead : _LIST_ENTRY LPVOID first = read64((char*)wnf_process_context_ptr + 0x40); LPVOID ptr; for (ptr = read64(read64((char*)wnf_process_context_ptr + 0x40)); ; ptr = read64(ptr)) { if (ptr ==

  62. Slide 62: Is it reliable enough?

    Is it reliable enough?

  63. Slide 63: Statistics

    Statistics SYSTEM shells – Number of times a SYSTEM shell was launched. Total LFH Writes – For all 100 runs of the exploit, how many corruptions were triggered. Avg LFH Writes – Average number of LFH overflows needed to obtain a SYSTEM shell. Failed after 32 – How many times the exploit failed to ov

  64. Slide 64: Spray Size Variation

    Spray Size Variation Result 3000 6000 10000 20000 SYSTEM shells 78 81 85 91 Total LFH writes 688 696 732 681 Avg LFH writes 8 8 8 7 Failed after 32 2 3 3 2 BSODs on exec 20 16 11 7 Unmapped Read 7 4 1 0 Increasing spray size leads to much decreased change of hitting unmapped reads. Average number of

  65. Slide 65: Exploit Demo!

    Exploit Demo! Command Prompt crosoft Windows [Version 10.@.19042.5@8] ) 2020 Microsoft Corporation. All rights reserved. C:\Users\test>cd Desktop C:\Users\test \Desktop>whoami desktop-o15vh7b\test C:\Users\test \Desktop>am

  66. Slide 66: How can this be found?

    How can this be found?

  67. Slide 67: Detection

    Detection Possible artefacts? NTFS Extended Attributes being created and queried. WNF objects being created (as part of the spray) Failed exploit attempts leading to BSODs 67 / 70

  68. Slide 68: NTFS Extended Attributes

    NTFS Extended Attributes 68 / 70

  69. Slide 69: Conclusion

    Conclusion Affects a wide range of Windows versions, however, prior to segment heap needs different exploitation techniques. Managed to get a 90% reliable exploit on the most recent Windows version with all mitigations on. However, from a practical purpose, there are better bugs which enable more re

  70. Slide 70: Credits

    Credits Boris Larin Cedric Halbronn and Aaron Adams Yan ZiShuang Alex Ionescu and Gabrielle Viala Corentin Bayet and Paul Fariello 70 / 70

Ncc Group — Investor Presentation on 6ix