来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享

admin 2021年5月17日08:38:36评论94 views字数 15263阅读50分52秒阅读模式

本文作者  Strawberry @ QAX CERT

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享

2020年7月,微软公开发布了Windows DNS Server远程代码执行漏洞,漏洞编号为CVE-2020-1350,该漏洞影响 2003 到 2019 年发布的所有 Windows Server 版本,CVSS 评分为满分 10 分。微软在公告中指出,该漏洞可引发蠕虫式传播。Windows DNS Server 在处理特制的 SIG 响应包时,存在远程代码执行漏洞,未经身份验证的攻击者可通过维护一个域名并设置指向恶意服务器的 NS 记录,通过向目标 DNS 服务器查询该域名的 SIG 来利用此漏洞,成功利用此漏洞的远程攻击者可在目标系统上以 SYSTEM 账户权限执行任意代码。本文对此漏洞进行分析,并参考链接 [2-3] 中的漏洞利用思路,在实验环境下拿到了 SYSTEM SHELL。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


声明:本篇文章由 Strawberry @ QAX CERT原创,仅用于技术研究,不恰当使用会造成危害,严禁违法使用 ,否则后果自负。


QAX CERT



漏洞简讯



2020年7月,微软公开发布了Windows DNS Server远程代码执行漏洞,漏洞编号为CVE-2020-1350,该漏洞影响 2003 到 2019 年发布的所有 Windows Server 版本,CVSS 评分为满分 10 分。微软在公告中指出,该漏洞可引发蠕虫式传播。Windows DNS Server 在处理特制的 SIG 响应包时,存在远程代码执行漏洞,未经身份验证的攻击者可通过维护一个域名并设置指向恶意服务器的 NS 记录,通过向目标 DNS 服务器查询该域名的 SIG 来利用此漏洞,成功利用此漏洞的远程攻击者可在目标系统上以 SYSTEM 账户权限执行任意代码。经研判,该漏洞无需交互、不需要身份认证且 Windows DNS Server 默认配置可触发。目前,互联网上已出现该漏洞相关细节、POC 以及漏洞利用视频。





漏洞分析



此漏洞公开后,Check Point 发布了相关的分析文章,传送门:

https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin:-exploiting-a-17-year-old-bug-in-windows-dns-servers。如果已了解漏洞原理可直接跳过本节。


以下为存在漏洞的函数 SigWireRead,SigWireRead 函数用来缓存 SIG 记录,当用户发起 SIG 查询时,DNS Server 首先查询有没有缓存记录,如果没有查询到或者该记录的 TTL 到期,则会向相应的 NS 服务器请求数据,这时候 DNS Server 会调用 SigWireRead 函数来存储这个记录。该漏洞是由整数溢出引发的堆溢出,漏洞位置位于倒数第 10 行和倒数第 3 行。

_BYTE *__fastcall SigWireRead(__int64 a1, __int64 pMsg, __int64 pchData, unsigned __int16 a4){  __int64 v4; // rbx  unsigned __int64 pchEnd; // rdi  unsigned __int8 *signameData; // r8  unsigned __int8 *v7; // rax  unsigned __int8 *v8; // rbp  size_t sigLength; // rdi  _BYTE *v10; // rax  _BYTE *v11; // rsi  unsigned __int8 nameSigner; // [rsp+30h] [rbp-138h]
v4 = pchData; pchEnd = pchData + a4; signameData = (unsigned __int8 *)(pchData + 0x12); if ( (unsigned __int64)signameData >= pchEnd ) return 0i64; v7 = Name_PacketNameToCountNameEx(&nameSigner, pMsg, signameData, pchEnd) v8 = v7; if ( !v7 ) return 0i64; sigLength = pchEnd - (_QWORD)v7; v10 = RR_AllocateEx((unsigned __int16)sigLength + (unsigned __int16)nameSigner + 0x14, 0, 0); //整数溢出 v11 = v10; if ( !v10 ) return 0i64; *(_OWORD *)(v10 + 0x38) = *(_OWORD *)v4; *((_WORD *)v10 + 0x24) = *(_WORD *)(v4 + 0x10); Name_CopyCountName(v10 + 0x4A, &nameSigner); memcpy(&v11[(unsigned __int8)v11[0x4A] + 0x4C], v8, sigLength); //堆溢出 return v11;}


RR_AllocateEx 函数的第一个参数为 sigLength + nameSigner + 0x14,由于其大小为16 bits(仅使用 CX),可导致在计算参数时产生整数溢出,分配较小的缓冲区,后续使用 memcpy 函数向缓冲区复制大量数据导致堆溢出。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


通过对比发现,关键变量 nameSigner 对应了数据包中 Signer's name 表示的域名长度,sigLength 则对应了 Signature 字段数据的长度。实际上 Signer's name 字段为 C0 0C,但解析成了 yyyyyyyyt.fun,这是因为使用了 DNS 名称压缩。C0 表示启用名称压缩,后面的 0C 代表了数据相对于 DNS 报文的偏移,Name_PacketNameToCountNameEx 函数会处理这个,并将处理结果放在 &nameSigner 处。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


正常 DNS 响应包大小不会超过 0xFFFF,基于 UDP 的 DNS 大小被限制到 512 字节,即使采用 TCP 连接,DNS 消息长度也被限制为 2 个字节,也就是 0xFFFF,如下图所示,这 0xFFFF 字节数据除了包含真实的 Signer's name 以及 Signature,还包括 DNS 头部和原始查询等信息,这些数据加起来已经明显超过0x14了,在这种情况下, sigLength + nameSigner + 0x14 小于 0x10000,因而不能直接产生溢出。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


所以嘛,还需要观察一下 Name_PacketNameToCountNameEx 函数,以 "yyyyyyyyt.fun" 为例,经过该函数处理后 nameSigner 为 0xF,表示后面有 0xF 个字节,后面的 2 表示后面有两组数据,一组为 9 字节长度的 yyyyyyyyt,另一组为 3 字节长度的 fun。计算出的 nameSigner 会参与后续计算。

0:001> db r8000002ea`760b445d  c0 0c 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0:001> r rcxrcx=000000f775b7f1f0
0:001> pdns!SigWireRead+0x41:00007ff7`a9daf911 488be8 mov rbp,rax0:001> db rcx // &nameSigner 000000f7`75b7f1f0 0f 02 09 79 79 79 79 79-79 79 79 74 03 66 75 6e ...yyyyyyyyt.fun000000f7`75b7f200 00 be 6c 75 ea 02 00 00-00 00 00 00 00 00 00 00 ..lu............
0:001> u rip l6dns!SigWireRead+0x4e:00007ff7`a9daf91e 482bf8 sub rdi,rax00007ff7`a9daf921 6683c114 add cx,14h00007ff7`a9daf925 33d2 xor edx,edx00007ff7`a9daf927 6603cf add cx,di00007ff7`a9daf92a 4533c0 xor r8d,r8d00007ff7`a9daf92d e8263c0800 call dns!RR_AllocateEx (00007ff7`a9e33558)
0:001> r cxcx=f0:001> ? rdi-raxEvaluate expression: 65472 = 00000000`0000ffc00:001> ? cx + ffc0 + 14 //无法整数溢出Evaluate expression: 65507 = 00000000`0000ffe3


正常情况下,即使 DNS 响应包长度为 0xFFFF 也无法触发整数溢出,但由于攻击者可控制 DNS 响应数据,可通过设置 Signer's name 中的偏移,使其指向一个较大的数来增加 nameSigner。由于每一组的长度不能超过 0x40(Name_PacketNameToCountNameEx 函数中有判断),所以我选择查询 9.yyyyyyyyt.fun。当偏移由 0xC 变为 0xD,要处理的数据就由 "01 39 09 79 79 79 79 79 79 79 79 74 03 66 75 6e" 变成了 "39 09 79 79 79 79 79 79 79 79 74 03 66 75 6e"。Name_PacketNameToCountNameEx 函数执行完成后 nameSigner 为 0x3b(0x39+1+1)。

0:001>dns!Name_PacketNameToCountNameEx+0xdd:00007ff7`a9d74f55 493bf6          cmp     rsi,r140:001> ub ripdns!Name_PacketNameToCountNameEx+0xbb:00007ff7`a9d74f33 4c8d46ff        lea     r8,[rsi-1]00007ff7`a9d74f37 0fb606          movzx   eax,byte ptr [rsi]00007ff7`a9d74f3a 488db530190000  lea     rsi,[rbp+1930h]00007ff7`a9d74f41 440fb6ca        movzx   r9d,dl00007ff7`a9d74f45 6641c1e108      shl     r9w,800007ff7`a9d74f4a 66440bc8        or      r9w,ax00007ff7`a9d74f4e 410fb7c1        movzx   eax,r9w00007ff7`a9d74f52 4803f0          add     rsi,rax    //rax=0xd
0:001> db rsi l40 000002ea`760b442d 39 09 79 79 79 79 79 79-79 79 74 03 66 75 6e 00 9.yyyyyyyyt.fun.000002ea`760b443d 00 18 00 01 c0 0c 00 18-00 01 00 00 00 00 ff d2 ................000002ea`760b444d 00 01 05 00 00 00 00 00-00 00 00 00 00 00 00 00 ................000002ea`760b445d 00 00 c0 0d 00 00 00 00-00 00 00 00 00 00 00 00 ................
0:001> gu //函数返回dns!SigWireRead+0x41:00007ff7`a9daf911 488be8 mov rbp,rax
0:001> db rcx l40 // &nameSigner000000f7`75b7f1f0 3b 01 39 09 79 79 79 79-79 79 79 79 74 03 66 75 ;.9.yyyyyyyyt.fu000000f7`75b7f200 6e 00 00 18 00 01 c0 0c-00 18 00 01 00 00 00 00 n...............000000f7`75b7f210 ff d2 00 01 05 00 00 00-00 00 00 00 00 00 00 00 ................000000f7`75b7f220 00 00 00 00 c0 0d 00 00-00 00 00 00 00 7f 00 00 ................


在这种情况下,sigLength + nameSigner + 0x14 的结果为 0x1000d,超过了 0xFFFF,成功整数溢出。虽然调用 RR_AllocateEx 函数时请求的大小为 0xd,但实际分配了 0x68 大小的缓冲区,因为分配的堆需要容纳 0x10 大小的自定义头部、0x38大小的 RR 头部以及重组的 DNS RR 数据(申请时的 0xd)。

0:001> u rip l6dns!SigWireRead+0x4e:00007ff7`a9daf91e 482bf8          sub     rdi,rax00007ff7`a9daf921 6683c114        add     cx,14h00007ff7`a9daf925 33d2            xor     edx,edx00007ff7`a9daf927 6603cf          add     cx,di00007ff7`a9daf92a 4533c0          xor     r8d,r8d00007ff7`a9daf92d e8263c0800      call    dns!RR_AllocateEx (00007ff7`a9e33558)0:001> r cxcx=3b0:001> ? cx + rdi - rax + 14    //整数溢出Evaluate expression: 65549 = 00000000`0001000d……0:001> dns!SigWireRead+0x5d:00007ff7`a9daf92d e8263c0800      call    dns!RR_AllocateEx (00007ff7`a9e33558)0:001> r rcxrcx=000000000000000d
//dns!Mem_Alloc00007ff7`a9e32f3a 3b3dfc850a00 cmp edi,dword ptr [dns!StandardAllocLists+0xc (00007ff7`a9edb53c)]00007ff7`a9e32f40 488d1de9850a00 lea rbx,[dns!StandardAllocLists (00007ff7`a9edb530)]00007ff7`a9e32f47 760c jbe dns!Mem_Alloc+0xe9 (00007ff7`a9e32f55)00007ff7`a9e32f49 488bc3 mov rax,rbx00007ff7`a9e32f4c 4883c358 add rbx,58h00007ff7`a9e32f50 3b7864          cmp     edi,dword ptr [rax+64h] ds:00007ff7`a9edb594=00000068
0:001> dq dns!StandardAllocLists + 58 00007ff7`a9edb588 000002ea`758c5b18 00000068`00000001 // 0x68 大小的 AllocLists00007ff7`a9edb598 0000000b`00000027 0000009c`0000000400007ff7`a9edb5a8 0000004e`000000df 00000000`00003f6000007ff7`a9edb5b8 ffffffff`ffffffff 00000000`ffffffff00007ff7`a9edb5c8 00000000`00000000 00000000`0000000000007ff7`a9edb5d8 00000000`00001388 000002ea`752c5098 // 0x88 大小的 AllocLists00007ff7`a9edb5e8 00000088`00000002 00000004`0000001e00007ff7`a9edb5f8 000000d2`00000007 00000021`000000ef
0:001> gudns!SigWireRead+0x62:00007ff7`a9daf932 488bf0 mov rsi,rax0:001> db rax-10 // 分配到 0x2ea758c5b18,长度为 0x68000002ea`758c5b18 1c 00 00 00 bb 1a 69 00-ef 0c 0c 0c 0c 0c 0c fe ......i.........000002ea`758c5b28 00 00 00 00 00 00 00 00-00 80 00 00 00 00 0d 00 ................000002ea`758c5b38 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................000002ea`758c5b48 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................000002ea`758c5b58 00 00 00 00 00 00 00 00-18 04 02 62 30 03 6f 72 ...........b0.or000002ea`758c5b68 67 0b 61 66 69 6c 69 61-73 2d 6e 73 74 03 6f 72 g.afilias-nst.or000002ea`758c5b78 67 00 00 00 00 00 00 00-00 00 00 00 ee 22 69 00 g............"i.000002ea`758c5b88 e8 5b 8c 75 ea 02 00 00-ef 0b 0b fe ef 0b 0b fe .[.u............


SigWireRead 函数在后面会将 RR 数据重组(图2框里的数据),但由于 Signer's name 实际指向的数据的长度就超过了 0xd,所以在 Name_CopyCountName 处就会产生溢出,覆盖到邻接的堆块。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


以下为后续调用 memcpy 函数向不可访问区域写数据时触发崩溃的场景:

0:001> dns!SigWireRead+0x98:00007ff7`a9daf968 e8324c0d00      call    dns!memcpy (00007ff7`a9e8459f)
0:001> db 2ea`758c5b18000002ea`758c5b18 1c 00 00 00 bb 1a 69 00-ef 0c 0c 0c 0c 0c 0c fe ......i.........000002ea`758c5b28 00 00 00 00 00 00 00 00-00 80 00 00 00 00 0d 00 ................000002ea`758c5b38 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................000002ea`758c5b48 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................000002ea`758c5b58 00 00 00 00 00 00 00 00-00 01 05 00 00 00 00 00 ................000002ea`758c5b68 00 00 00 00 00 00 00 00-00 00 3b 01 39 09 79 79 ..........;.9.yy000002ea`758c5b78 79 79 79 79 79 79 74 03-66 75 6e 00 00 18 00 01 yyyyyyt.fun..... //已经溢出了000002ea`758c5b88 c0 0c 00 18 00 01 00 00-00 00 ff d2 00 01 05 00 ................
0:001> r r8 //但还是要复制0xffbe长度的数据r8=000000000000ffbe
0:001> p(868.8fc): Access violation - code c0000005 (first chance)First chance exceptions are reported before any exception handling.This exception may be expected and handled.msvcrt!memcpy+0xb4:00007fff`98f74a34 660f7f49f0 movdqa xmmword ptr [rcx-10h],xmm1 ds:000002ea`758cb000=????????????????????????????????





漏洞利用



DNS 堆管理

RR_AllocateEx 函数被用来申请 RR 对象空间,在函数内部会将申请的长度加上 a2(例,SigWireRead 调用 RR_AllocateEx 时将 a2 设置为0),然后再加上 0x38(RR 对象头长度),然后调用 Mem_Alloc 函数。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


如下所示,在 Mem_Alloc 函数中会将传入的 length 加 0x10(自定义堆头大小)。如果这个长度大于0xA0,会调用 allocMemory -> HeapAlloc 进行分配。而如果长度在 0xA0 以内,则从 StandardAllocLists 中分配合适大小的堆,如果当前 StandardAllocLists 中没有合适大小的堆,就会调用 allocMemory -> HeapAlloc 函数请求 v14[3] * v14[4] 大小的堆,并将它们分开并链接起来供 StandardAllocLists 查询。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


StandardAllocLists 维护四个不同大小的堆链,包括 0x50、0x68、0x88 和 0xA0。例,如果 v8 在 0x89 到 0xA0 之间,则会分配到 0xA0 大小的 DNS 堆:

index 0:0x50 * 0x33(个数)= 0xFF0(0x00〜0x50) 

index 1:0x68 * 0x27(个数)= 0xFD8(0x51〜0x68) 

index 2:0x88 * 0x1e(个数)= 0xFF0(0x69〜0x88) 

index 3:0xA0 * 0x19(个数)= 0xFA0(0x89〜0xA0)

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


对象的分配与释放

由前面信息可知,RRSIG 对象包括 0x10 的自定义头部、0x38 字节的对象头部以及不确定长度的对象数据(包括 0x12 长度的数据、Signer's name 以及 Signature)。通过调整发送的响应包的 Signature 的长度可分配到目标大小的堆,如下,RRSIG 对象被分配到 0xA0 大小的块上。由于 RR 对象的结构已知,所以也很容易伪造。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


当缓存信息超时后,将会自动释放该 RR 对象。可将目标 DNS 响应数据的 TTL 设置为 0,并在短时间内再次请求,从而释放原有的堆,同时也会重新申请一个新的堆来储存数据。以下为释放后的堆,其偏移 8 处指向下一个大小为 0xA0 的可用的堆。由于其大小在 0xA0 以内,因而会被 StandardAllocLists 记录(0xA0 大小,索引 3),也是遵循后进先出的原则。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


避免立即崩溃

为了避免漏洞触发时引发崩溃,可以多次请求服务器使其分配很多 RR 对象(保证这些对象在漏洞利用时不会被释放)。然后释放中间的一个对象,使得触发漏洞时,RRSIG 对象可以申请到之前释放的块。


1、分配大量 0x1000F 块(加上 0x10 字节的 _Heap_Entry 实际上是 0x10020),然后释放中间的一个 0x1000F 块

2、分配大小为 0xF05F 的块,从而分割出 0xFB0 大小的剩余可用空间

3、接下来再请求大量 0xA0 块就可以使目标 RR 缓冲区分配到这个 0xFB0 大小的块(_Heap_Entry 0x10 + 0xFA0)


这样可保证向重占位的 0xFB0 大小的块中的某个释放后的对象处复制超过 0xFFFF 大小的数据也不会触发崩溃。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


某次测试时的布局如下,我们可以释放 sp_115 所在堆,然后利用漏洞申请相同大小的堆,并覆盖后面的数据。

0:019> db 17ef599f300 lfa00000017e`f599f300  00 00 00 00 00 00 00 00-42 71 5e fc f9 36 db 10  ........Bq^..6..0000017e`f599f310  70 b7 65 e7 bb 22 a3 00-ef 0c 0c 0c 0c 0c 0c fe  p.e.."..........0000017e`f599f320  00 00 00 00 00 00 00 00-61 80 10 00 18 00 57 00  ........a.....W.0000017e`f599f330  57 02 00 00 57 02 00 00-00 00 00 00 01 00 00 00  W...W...........0000017e`f599f340  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f350  00 00 00 00 00 00 00 00-00 01 05 00 00 00 00 00  ................0000017e`f599f360  00 00 00 00 00 00 00 00-00 00 16 03 06 73 70 5f  .............sp_0000017e`f599f370  31 31 35 09 79 79 79 79-79 79 79 79 74 03 66 75  115.yyyyyyyyt.fu0000017e`f599f380  6e 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  n...............0000017e`f599f390  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f3a0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f3b0  00 00 00 00 bb 22 a3 00-ef 0c 0c 0c 0c 0c 0c fe  ....."..........0000017e`f599f3c0  00 00 00 00 00 00 00 00-61 80 00 00 18 00 57 00  ........a.....W.0000017e`f599f3d0  57 02 00 00 c7 19 00 00-00 00 00 00 01 00 00 00  W...............0000017e`f599f3e0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f3f0  00 00 00 00 00 00 00 00-00 01 05 00 00 00 00 00  ................0000017e`f599f400  00 00 00 00 00 00 00 00-00 00 16 03 06 73 70 5f  .............sp_0000017e`f599f410  31 31 36 09 79 79 79 79-79 79 79 79 74 03 66 75  116.yyyyyyyyt.fu0000017e`f599f420  6e 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  n...............0000017e`f599f430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f450  00 00 00 00 bb 22 a3 00-ef 0c 0c 0c 0c 0c 0c fe  ....."..........0000017e`f599f460  00 00 00 00 00 00 00 00-61 80 00 00 18 00 57 00  ........a.....W.0000017e`f599f470  57 02 00 00 c7 19 00 00-00 00 00 00 01 00 00 00  W...............0000017e`f599f480  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f490  00 00 00 00 00 00 00 00-00 01 05 00 00 00 00 00  ......................


数据泄露

通过设置 RR 对象中的 rr_size 可泄露后面对象的堆数据。当客户端发起 SIG 请求时,DNS Server 会从缓存中读取数据。如下,在 SigWireWrite 函数中,rr_size 可控制读取数据的长度,但要需要保证 rr_size - nameSigner - 0x14 的大小不超过 v4 - v9(Wire_TestForBytesRemaining 函数控制)。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


将 sp_116 的 rr_size 设置为 0x1a0(本来是 0x57)然后请求其 SIG 可获取sp_117 和 sp_118  范围内的数据,如下:

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


代码执行

当使用 RR_Free 函数释放超时的 RR 对象时,会判断 wtype 是否为 2(NS)或 6(SOA),如果是的话,就会调用 Timeout_FreeWithFunctionEx,在该函数中调用 Mem_Alloc 申请 0x38 大小的堆,用于存放对象指针(pItem)、释放函数指针(pFreeFunction)等等。对象释放时会在Timeout_CleanupDelayedFreeList 函数中调用 FreeFunction,且参数为 pItem,不过这通常需要一段时间。

// DNS!RR_Free  v4 = *(_WORD *)(v1 + 0xC);  if ( v4 != 2 && v4 != 6 && (unsigned __int16)(v4 + 0xFF) > 1u || (v5 = *(_WORD *)(v1 + 0xA), v5 & 0x200) )  { ...... }  else  {        *(_WORD *)(v1 + 0xA) = v5 | 0x200;        Timeout_FreeWithFunctionEx(v1, (__int64)RR_Free, (__int64)"nanoserver\ds\dns\server\server\record.c", 168);        _InterlockedAdd(&dword_1401CCF4C, 1u);  }//DNS!Timeout_FreeWithFunctionEx  if ( rr_object )  {    v4 = (CHAR *)a3;    v5 = (void *)rr_free;    v6 = a4;    v7 = (void *)rr_object;    ......    v8 = (DnsTimeoutObject *)Mem_Alloc(0x28, 7i64, (__int64)"nanoserver\ds\dns\server\server\timeout.c", 1619);    v9 = v8;    if ( v8 )     {      v8->Tag = 0xDE1AEDFE;       v8->pItem = v7;          //偏移0x8处设置需要释放的对象指针      v8->pFreeFunction = v5;  // 偏移0x10处设置RR_FREE函数指针      v8->pszFile = v4;      v8->LineNo = v6;//DNS!Timeout_CleanupDelayedFreeList    if ( v5->pFreeFunction )    {      _InterlockedIncrement(&dword_1401DB464)      ((void (__usercall *)(__int64 (*)(void)@<rcx>))v5->pFreeFunction)((__int64 (*)(void))v6->pItem);      }    else    {      Mem_Free((_QWORD *)v6->pItem, 0i64, 0i64, (__int64)"nanoserver\ds\dns\server\server\timeout.c", 571);    }


如果可以将 DnsTimeoutObject(0x38大小)分配到可控的位置,再利用溢出覆盖 pItem 和 pFreeFunction,就可以在对象释放时控制执行流程(允许执行带一个参数的函数)。而且,原始的 pFreeFunction 指向 dns!RR_Free,可通过将其泄露从而进一步获取 DNS 基址(这需要收集不同版本的偏移)。

0:021> db 17e`f599f630 l500000017e`f599f630  00 00 00 00 bb 07 50 00-ef 0c 0c 0c 0c 0c 0c fe  ......P.........0000017e`f599f640  10 16 03 e7 7e 01 00 00-70 9f 02 e7 7e 01 00 00  ....~...p...~...0000017e`f599f650  b0 4a e4 d8 f7 7f 00 00-b8 9f f6 d8 f7 7f 00 00  .J..............0000017e`f599f660  fe ed 1a de a8 00 00 00-00 00 00 00 00 00 00 00  ................0000017e`f599f670  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:021> u 7ff7`d8e44ab0 //偏移 0x10 处(不包括头部)的函数指针dns!RR_Free:00007ff7`d8e44ab0 4885c9 test rcx,rcx00007ff7`d8e44ab3 0f8434020000 je dns!RR_Free+0x23d (00007ff7`d8e44ced)00007ff7`d8e44ab9 48895c2408 mov qword ptr [rsp+8],rbx00007ff7`d8e44abe 48896c2410 mov qword ptr [rsp+10h],rbp00007ff7`d8e44ac3 4889742418 mov qword ptr [rsp+18h],rsi00007ff7`d8e44ac8 57 push rdi00007ff7`d8e44ac9 4154 push r1200007ff7`d8e44acb 4156 push r14
0:021> db 17e`e7029f70-10 //偏移 0x8 处的一个 SOA 对象指针0000017e`e7029f60 00 00 00 00 bb 22 8a 00-ef 0c 0c 0c 0c 0c 0c fe ....."..........0000017e`e7029f70 00 00 00 00 00 00 00 00-61 80 10 22 06 00 2d 00 ........a.."..-.0000017e`e7029f80 ae 01 00 00 ae 01 00 00-00 00 00 00 01 00 00 00 ................0000017e`e7029f90 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................0000017e`e7029fa0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................0000017e`e7029fb0 00 00 00 00 00 00 00 00-00 00 00 00 01 00 00 14 ................0000017e`e7029fc0 03 04 73 70 5f 30 09 79-79 79 79 79 79 79 79 74 ..sp_0.yyyyyyyyt0000017e`e7029fd0 03 66 75 6e 00 00 00 00-00 00 00 00 00 00 00 00 .fun............


先前,研究人员已经找到了 CFG 允许执行的且只需要一个参数的 dns!NsecDNSRecordConvert 函数,它可以将第一个参数偏移0x20处的字符串复制到新开辟的区域,这允许我们进行任意地址读取。我们可以通过设置 v1 偏移 0x28 处的数据使 Rpc_AllocateRecord 函数申请到我们可控的区域,然后再使用数据泄露思路进行读取。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享


为了获得 msvcrt!system 函数(CFG允许)的地址,可通过上述思路进一步泄露 msvcrt!memcpy 函数地址,然后根据偏移计算出 system 函数地址。但这还需要一个可控的参数地址,可通过连续释放两个对象,然后泄露后一个对象空间来获得堆指针,将 dns!_imp_memcpy 地址布局到该地址偏移 0x20 处。成功获取 msvcrt!system 函数地址后,采用同样的思路调用 system 可获得任意命令执行。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享





总结



Windows DNS Server 在处理特制的 SIG 响应包时,存在远程代码执行漏洞,未经身份验证的攻击者可通过向目标 DNS 服务器查询并响应 SIG 请求来利用此漏洞,成功利用此漏洞的远程攻击者可在目标系统上以 SYSTEM 账户权限执行任意代码。本文对此漏洞进行分析,并参考链接 [2-3] 中的漏洞利用思路,在实验环境下拿到了 SYSTEM SHELL,如下所示。PS:实验环境是指将 DNS 服务器转发器配置为攻击者控制的 NS 服务器。由于真实环境下的 DNS 服务器会处理大量请求,此思路可能无法很好地应用于实战。

来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享





参考链接



[1]https://msrc.microsoft.com/update-guide/vulnerability/CVE-2020-1350


[2]http://blog.diffense.co.kr/2020/12/04/Sigred.html


[3]https://datafarm-cybersecurity.medium.com/exploiting-sigred-cve-2020-1350-on-windows-server-2012-2016-2019-80dd88594228


[4]https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin:-exploiting-a-17-year-old-bug-in-windows-dns-servers


[5]https://msrc.microsoft.com/update-guide/vulnerability/CVE-2020-1350


[6]https://mp.weixin.qq.com/s/4HBVvDuq_HWXop14ENUSuA





来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享

奇安信CERT长期招募安全研究员

↓↓↓向下滑动图片了解更多↓↓↓


本文始发于微信公众号(奇安信 CERT):来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年5月17日08:38:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   来看!年度热门漏洞细节,CVE-2020-1350 Windows DNS Server 远程代码执行漏洞技术分享http://cn-sec.com/archives/257691.html

发表评论

匿名网友 填写信息