作者:lu4nx@知道創宇404積極防御實驗室
時間:2021年1月25日

最近比較火的一個漏洞,Windows 10 中通過瀏覽器等途徑直接訪問路徑 \\.\globalroot\device\condrv\kernelconnect 時,會導致系統藍屏。

漏洞分析

在「此電腦」 > 「屬性」 > 「高級系統設置],選擇「啟動和故障恢復」的「設置」,設置「寫入調試信息」為「小內存轉儲(256KB)」,然后訪問路徑:\\.\globalroot\device\condrv\kernelconnect。藍屏后重啟系統,用 WinDbg 載入 C:\Windows\Minidump 下的剛轉儲的 .dmp 文件,并執行:

!analyze -v

從輸出結果中找到調用棧,如下:

PROCESS_NAME:  explorer.exe

STACK_TEXT:
ffffd28c`2d35d2d0 fffff805`47037159 : 00000000`00000000 fffff805`4703704d 00000000`00000000 00000000`00000000 : condrv!CdpDispatchCleanup+0x1f
ffffd28c`2d35d300 fffff805`475d87b8 : 00000000`00000000 ffff8909`f300e080 00000000`00000000 ffff8909`f04cf9a0 : nt!IofCallDriver+0x59
ffffd28c`2d35d340 fffff805`47601ddc : 00000000`00000000 ffffd28c`2d35d890 00000000`00000000 00000000`00000000 : nt!IopCloseFile+0x188
ffffd28c`2d35d3d0 fffff805`475f666f : ffff8909`eff699f0 00000000`00000000 ffff8909`eb813490 00000000`00000001 : nt!IopParseDevice+0x1f3c
ffffd28c`2d35d540 fffff805`475f4ad1 : ffff8909`eb813400 ffffd28c`2d35d788 00000000`00000040 ffff8909`e9cbd140 : nt!ObpLookupObjectName+0x78f
ffffd28c`2d35d700 fffff805`476b1afb : ffff8909`00000001 00000000`1034d0a0 00000000`00000001 00000000`1034e25c : nt!ObOpenObjectByNameEx+0x201
ffffd28c`2d35d840 fffff805`471d5355 : ffff8909`f0ee5080 00000000`00000000 ffff8909`f0ee5080 00000000`1034e25c : nt!NtQueryAttributesFile+0x1eb
ffffd28c`2d35db00 00007ffa`a4ddce94 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x25
00000000`1034d038 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x00007ffa`a4ddce94

最終執行到以下指令時引發藍屏:

condrv!CdpDispatchCleanup+0x1f

查看 condrv!CdpDispatchCleanup+0x1f 的匯編代碼:

1: kd> u condrv!CdpDispatchCleanup+0x1f
condrv!CdpDispatchCleanup+0x1f:
fffff805`8e31b04f 488b01          mov     rax,qword ptr [rcx]
fffff805`8e31b052 4c8b4820        mov     r9,qword ptr [rax+20h]
fffff805`8e31b056 4d85c9          test    r9,r9
fffff805`8e31b059 7521            jne     condrv!CdpDispatchCleanup+0x4c (fffff805`8e31b07c)
fffff805`8e31b05b 33c9            xor     ecx,ecx
fffff805`8e31b05d 894a30          mov     dword ptr [rdx+30h],ecx
fffff805`8e31b060 48894a38        mov     qword ptr [rdx+38h],rcx
fffff805`8e31b064 33d2            xor     edx,edx

注意第一句 mov 的尋址地址取的是 RCX 寄存器中的值,從上面的分析結果就翻到發生事故時各個寄存器的值:

CONTEXT:  ffffd28c2d35c8e0 -- (.cxr 0xffffd28c2d35c8e0)
rax=ffff8909f04cfab8 rbx=ffff8909f300e080 rcx=0000000000000000
rdx=ffff8909f04cf9a0 rsi=0000000000000000 rdi=ffff8909f04cf9a0
rip=fffff8058e31b04f rsp=ffffd28c2d35d2d0 rbp=0000000000000000
 r8=ffff8909f04cf9a0  r9=ffff8909eff699f0 r10=fffff8058e31b030
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=ffff8909eff699f0
iopl=0         nv up ei ng nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010282
condrv!CdpDispatchCleanup+0x1f:
fffff805`8e31b04f 488b01          mov     rax,qword ptr [rcx] ds:002b:00000000`00000000=????????????????

可以看到 RCX 指向的空地址,當 mov 指令尋址時,由于 RCX 為空地址,因此引發了空指針異常導致藍屏。

查看 CdpDispatchCleanup 函數完整的匯編代碼:

1: kd> uf condrv!CdpDispatchCleanup
condrv!CdpDispatchCleanup:
fffff805`8e31b030 4883ec28        sub     rsp,28h
fffff805`8e31b034 488b82b8000000  mov     rax,qword ptr [rdx+0B8h]
fffff805`8e31b03b 4c8bc2          mov     r8,rdx
fffff805`8e31b03e 488b4830        mov     rcx,qword ptr [rax+30h]
fffff805`8e31b042 4885c9          test    rcx,rcx
fffff805`8e31b045 0f84250e0000    je      condrv!CdpDispatchCleanup+0xe40 (fffff805`8e31be70)  Branch

condrv!CdpDispatchCleanup+0x1b:
fffff805`8e31b04b 488b4918        mov     rcx,qword ptr [rcx+18h]
fffff805`8e31b04f 488b01          mov     rax,qword ptr [rcx]
fffff805`8e31b052 4c8b4820        mov     r9,qword ptr [rax+20h]
fffff805`8e31b056 4d85c9          test    r9,r9
fffff805`8e31b059 7521            jne     condrv!CdpDispatchCleanup+0x4c (fffff805`8e31b07c)  Branch

condrv!CdpDispatchCleanup+0x2b:
fffff805`8e31b05b 33c9            xor     ecx,ecx
fffff805`8e31b05d 894a30          mov     dword ptr [rdx+30h],ecx
fffff805`8e31b060 48894a38        mov     qword ptr [rdx+38h],rcx
fffff805`8e31b064 33d2            xor     edx,edx
fffff805`8e31b066 498bc8          mov     rcx,r8
fffff805`8e31b069 48ff15a8b0ffff  call    qword ptr [condrv!_imp_IofCompleteRequest (fffff805`8e316118)]
fffff805`8e31b070 0f1f440000      nop     dword ptr [rax+rax]
fffff805`8e31b075 33c0            xor     eax,eax

condrv!CdpDispatchCleanup+0x47:
fffff805`8e31b077 4883c428        add     rsp,28h
fffff805`8e31b07b c3              ret

condrv!CdpDispatchCleanup+0x4c:
fffff805`8e31b07c 8b10            mov     edx,dword ptr [rax]
fffff805`8e31b07e 498bc1          mov     rax,r9
fffff805`8e31b081 482bca          sub     rcx,rdx
fffff805`8e31b084 498bd0          mov     rdx,r8
fffff805`8e31b087 ff152bb2ffff    call    qword ptr [condrv!_guard_dispatch_icall_fptr (fffff805`8e3162b8)]
fffff805`8e31b08d ebe8            jmp     condrv!CdpDispatchCleanup+0x47 (fffff805`8e31b077)  Branch

condrv!CdpDispatchCleanup+0xe40:
fffff805`8e31be70 33c9            xor     ecx,ecx
fffff805`8e31be72 c742300d0000c0  mov     dword ptr [rdx+30h],0C000000Dh
fffff805`8e31be79 48894a38        mov     qword ptr [rdx+38h],rcx
fffff805`8e31be7d 33d2            xor     edx,edx
fffff805`8e31be7f 498bc8          mov     rcx,r8
fffff805`8e31be82 48ff158fa2ffff  call    qword ptr [condrv!_imp_IofCompleteRequest (fffff805`8e316118)]
fffff805`8e31be89 0f1f440000      nop     dword ptr [rax+rax]
fffff805`8e31be8e b80d0000c0      mov     eax,0C000000Dh
fffff805`8e31be93 e9dff1ffff      jmp     condrv!CdpDispatchCleanup+0x47 (fffff805`8e31b077)  Branch

主要關注觸發空指針異常的那段邏輯代碼:

fffff805`8e31b034 488b82b8000000  mov     rax,qword ptr [rdx+0B8h]
fffff805`8e31b03b 4c8bc2          mov     r8,rdx
fffff805`8e31b03e 488b4830        mov     rcx,qword ptr [rax+30h] // RCX 是 RAX 指向的結構體成員
fffff805`8e31b042 4885c9          test    rcx,rcx
fffff805`8e31b045 0f84250e0000    je      condrv!CdpDispatchCleanup+0xe40 (fffff805`8e31be70)
fffff805`8e31b04b 488b4918        mov     rcx,qword ptr [rcx+18h] // RCX+18h 是 RCX 的成員變量
fffff805`8e31b04f 488b01          mov     rax,qword ptr [rcx] // 由于這里沒判斷 RCX+18h 是否為空,導致 bug 發生

從對 RDX、RAX、RCX 指針的偏移引用來看,這 3 個寄存器應該是都是指向的結構體,而 RCX+18h 這個成員變量指向的空地址。

CdpDispatchCleanup 是派遣函數,只有需要處理 IRP 時才會調用;而 RCX+18h 這個成員函數為 0 應該賦值失敗導致的,所以覺得問題應該出現在創建分發函數時出的問題。

把 C:\Windows\System32\drivers\condrv.sys 拖到 Ghidra 里去分析,根據"\KernelConnect"路徑找到對應的創建函數 CdCreateKernelConnection:

longlong * CdCreateKernelConnection(PIRP irp)
{
  ...省略..

  if (irp->RequestorMode != '\0') {
    return (longlong *)0xc0000022;
  }

  lVar4 = *(longlong *)&(irp->Tail).field_0x40;
  puVar9 = CdpFindEaBufferItem(*(uint **)((longlong)&irp->AssociatedIrp + 4),"attach");
  if ((puVar9 == (uint *)0x0) || (*(short *)((longlong)puVar9 + 6) != 8)) {
    return (longlong *)0xc000000d;
  }
  ...省略...
}

根據"參考文章"中的提點,找到了漏洞觸發的原因是這段:

if (irp->RequestorMode != '\0') {
  return (longlong *)0xc0000022;
}

I/O 控制函數都會返回一個狀態碼,參考 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 十六進制對應的狀態碼,0xc0000022 對應為 STATUS_ACCESS_DENIED,這段邏輯代碼用來判斷 irp->RequestorMode 是否來自應用層,對于應用層的訪問則返回 STATUS_ACCESS_DENIED 來拒絕,但是這里沒有調用 IoCompleteRequest 來結束請求,因此才導致來后面會調用到派遣函數 CdpDispatchCleanup。正確的寫法應該類似為:

if (irp->RequestorMode != '\0') {
  irp->IoStatus.Status = STATUS_ACCESS_DENIED;
  IoCompleteRequest(irp, IO_NO_INCREMENT);
  return STATUS_ACCESS_DENIED;
}

參考

《關于此次condrv.sys拒絕服務漏洞分析》:https://www.secpulse.com/archives/151714.html


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1469/