原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

ADLab發表於2020-07-06

一、前言


國外安全研究人員在由Treck開發的TCP/IP協議棧中發現了多個漏洞,這一系列漏洞統稱為Ripple20。這些漏洞廣泛存在於嵌入式和物聯網裝置中,影響了多個行業領域(包括醫療、運輸、能源、電信、工業控制、零售和商業等),涉及了眾多供應商(包括HP、Schneider Electric、Intel、Rockwell Automation、Caterpillar、Baxter等)。


這些漏洞源於Ripple20的多個協議(包括IPv4、ICMPv4、IPv6、IPv6OverIPv4、TCP、UDP、ARP、DHCP、DNS或乙太網鏈路層)在處理網路報文傳送時存在缺陷,其中包括四個嚴重漏洞,它們的CVE編號分別為CVE-2020-11896、CVE-2020-11898、CVE-2020-11910、CVE-2020-11911。CVE-2020-11896(CVSS評分10)可導致遠端執行程式碼,CVE-2020-11897(CVSS評分10)可導致越界寫入,CVE-2020-11901(CVSS評分9)可導致遠端執行程式碼,CVE-2020-11898(CVSS評分9.1)可導致洩露敏感資訊。其它15個Ripple20漏洞的嚴重程度各異,CVSS評分分別從3.1到8.2。

由於物聯網裝置供應鏈的特性,漏洞影響的裝置眾多,影響範圍廣且持續時間長,漏洞修復的實施較困難。因此,啟明星辰ADLab第一時間對相關漏洞進行了分析並提出了防範建議。


二、協議棧檢測


由於採用Treck協議棧的廠家較多,有些廠家是硬體IP核的方式引用了Treck協議棧。單純通過裝置指紋來識別漏洞是不足的,如何檢測目標裝置是否為Treck協議棧成為資產排查的關鍵,為此啟明星辰ADLab安全研究員對Treck協議棧進行了深入分析,並公開了Treck協議棧指紋檢測方法發現漏洞。


Treck協議棧自定義了型別為165(0xa5)的ICMP包,並一旦收到165的ICMP包會回覆型別為166的ICMP包響應。如下程式碼所示:


原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

首先,向目標傳送 ICMP請求包,其中type=0xa5,code=0。如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

然後,接收目標返回的icmp響應包資料,其中type =0xa6,code =0,ICMP報文第9位元組後的六個位元組為0x01,0x51,0x35,0x28,0x57,0x32(大端)或0x51,0x01,0x28,0x35,0x32,0x57(小端)。


滿足上述的條件,則表明目標裝置為treck 協議棧。如下圖所示:


原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


三、防範建議


1、應用更新

及時更新到Treck TCP/IP協議棧軟體的最新穩定版本(6.0.1.67或更高版本)。


2.  阻止異常IP流量

可以通過深度資料包檢查來阻止網路攻擊,以下是可以適當應用於網路環境中的可能緩解措施,過濾選項包括:

●       如果網路環境不支援,則規範化或拒絕IP分片的資料包(IP分片)

●       如果不需要,請禁用或阻止IP隧道(IPv6-in-IPv4或IP-in-IP隧道)

●       阻止IP源路由和所有不贊成使用IPv6的功能,例如路由標頭

●       強制執行TCP檢查並拒絕格式錯誤的TCP資料包

●       阻止未使用的ICMP控制訊息,例如MTU更新和地址掩碼更新

●       通過安全的遞迴伺服器或應用層防火牆規範DNS

●       確保網路環境中使用的是可靠的OSI第2層裝置(乙太網)

●       通過DHCP偵聽等功能提供DHCP / DHCPv6安全性

●       如果未在交換基礎架構中使用,則禁用或阻止IPv6多播。


四、相關概念介紹


1、IP分片

IP分片使得在網路中傳送大的IP包成為可能,即使其大小大於網路特定鏈路中允許的最大值。IP分片技術是一種將資料包分成幾個較小的部分以支援通過這些鏈路和網路傳輸的技術。該協議支援在傳送端進行分片,然後在接收端對分片重新組合。這允許不同的包在網路中零散地傳輸,並在另一側正確地重新組裝。

不同的包使用IP頭中的標識欄位(Identification)進行分組。此標識欄位描述分片屬於哪個包。同一個包的多個分片的Identification是一樣的。IPv4通過Flags及Fragment Offset欄位對分片進行管理,Flags由R、DF、MF三部分組成:

●       R(Reserve bit)保留未用

●       DF (Don't Fragment) DF =1:禁止分片 , DF =0:允許分片

●       MF (More Fragment) MF =1:非最後一片, MF =0:最後一片(或未分片)

Fragment Offset(13位):一個IP分組分片封裝原IP分組資料的相對偏移量, 片偏移欄位以8位元組為單位。IP包結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


2、IP隧道技術

IP隧道允許兩個獨立網路之間的虛擬點到點鏈路。它是通過將包(可以是IP包)封裝在另一個包中來實現的,使得內部包具有與外部包不同的源地址和目標地址。外部包的源地址和目標地址是隧道端點,內部包中的地址用於隧道兩端的網路路由。隧道入口點是接收應通過隧道轉發的IP資料包的節點。它將此資料包封裝在外部IP資料包中。當資料包到達隧道出口點時,它被解封裝並轉發,就好像它是在目標網路中傳送的常規資料包一樣。IP-in-IP包如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

IP隧道技術主要應用在虛擬專用網(VPN)技術中。目前有幾種隧道協議,其中最簡單和最古老的是IP-in-IP(IP協議編號4)。IP-in-IP是一種IP隧道協議,其中一個IP包通過新增一個外部IP報頭(其源地址和目標地址分別等於隧道的入口點和出口點)封裝在另一個IP包中。內部資料包未被修改,外部IP頭從內部IP頭複製一些欄位。外部報頭的IP協議號為4。IP-in-IP報文示例如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


五、Treck協議棧


1、協議棧概述


Treck協議棧通過tsPacket結構來描述包結構,通過tsUserPacket結構支援資料包分片。這兩個結構體在treck/include/trsocket.h檔案中定義。Treck TCP/IP協議棧中的包資料由tsPacket的結構表示。每個包都與一個資料緩衝區相關聯,該資料緩衝區儲存從介面驅動程式到達的原始資料。tsPacket結構還儲存另一個稱為ttUserPacket的重要結構,以及指向tsSharedData結構的指標,該結構包括網路協議棧處理資料包時所需的資訊(指向套接字結構、src/dst地址或埠等的指標)。定義如下:

struct tsPacket {

ttUserPacket pktUserStruct;

ttSharedDataPtr pktSharedDataPtr;

struct tsPacket * pktChainNextPtr;

struct tsDeviceEntry * pktDeviceEntryPtr;

union anon_union_for_pktPtrUnion pktPtrUnion;

tt32Bit pktTcpXmitTime;

tt16Bit pktUserFlags;

tt16Bit pktFlags;

tt16Bit pktFlags2;

tt16Bit pktMhomeIndex;

tt8Bit pktTunnelCount;

tt8Bit pktIpHdrLen;

tt8Bit pktNetworkLayer;

tt8Bit pktFiller[1];

};

這是包含的ttUserPacket結構(tsUserPacket的typedef),定義如下:

struct tsUserPacket {

void * pktuLinkNextPtr; // Next tsUserPacket for fragmented data

ttUser8BitPtr pktuLinkDataPtr;

ttPktLen pktuLinkDataLength;

ttPktLen pktuChainDataLength;

int pktuLinkExtraCount;

};

pktuLinkNextPtr :用於跟蹤資料包中的分片。此欄位指向表示下一個分片的另一個tsPacket結構,該tsPacket還儲存對下一個分片的引用,如果此連結是最後一個分片,或者資料未被分片,則此欄位將為NULL。

pktuLinkDataPtr:指向當前分片的資料緩衝區。當Treck協議棧在不同階段處理資料包時,資料緩衝區中的確切位置會發生變化,這取決於當前正在處理的資料包所在協議層。例如,當Treck協議棧處理乙太網層(在tfEtherRecv()函式中)時,此欄位指向乙太網報頭。

pktuLinkDataLength:pktuLinkDataPtr指向的資料的大小,即單個分片的大小。

pktuChainDataLength:表示包含所有分片的資料包長度,即資料包的總大小。它只為第一個分片設定。如果資料沒有分片,則等於pktuLinkDataLength。


2、協議棧處理過程


協議棧中的一個常見模式是在協議棧中的層之間移動時調整pktuLinkDataPtr指標。例如,如果我們的包是一個ICMP回顯請求包(ping),它的協議由三層組成:Ethernet、IPv4、ICMP。在這種情況下,當處理乙太網層(在tfEtherRecv()函式中)時,pktuLinkDataPtr指向乙太網報頭的開始,然後在移動到下一層之前,使用以下程式碼對其進行調整,如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


在本例中,0xe(十進位制為14)是乙太網報頭(6(dst MAC)+6(src MAC)+2(etherType))的大小。當tfEtherRecv()函式完成包處理時,它將包轉發到下一層處理。支援的乙太網型別有ARP、IPv4和IPv6。如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

在示例中,當IPv4層接收到資料包(在函式tfIpIncomingPacket()函式中)時,指標pktuLinkDataPtr已經指向IP報頭。傳入資料由具有相同命名約定tf*IncomingPacket的函式處理,其中*是協議名。對於ICMP包來說,它由三層協議組成(Ethernet/IPv4/ICMP),資料包將由函式tfEtherRecv、tfIpIncomingPacket和tfIcmpIncomingPacket函式分別處理。


3、分片重組

Treck協議棧在tfIpReassemblePacket()函式中處理分片的重組,該函式由tfIpIncomingPacket()呼叫。每當接收到發往裝置的IP分片時,就會呼叫此函式。如果缺少分片,函式將返回NULL。否則,如果所有分片都到達並且沒有空洞,則網路協議棧將使用pktuLinkNextPtr欄位將分片連結在一起,然後將資料包傳遞給下一層進行進一步處理。在此上下文中,“重組”一詞並不意味著將資料包複製到連續的儲存塊,而只是簡單地將它們連結到一個連結串列中。分片資料連結串列結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


4、tfIpIncomingPacket函式

tfIpIncomingPacket()函式是處理IP包的主要函式,該函式主要流程如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


tfIpIncomingPacket()首先判斷資料包合法性。tfIpIncomingPacket()函式除了驗證IP頭校驗和,它還進行以下驗證,如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


然後如果所有合法性檢查都通過,tfIpIncomingPacket()函式將檢查IP報頭中TotalLength 是否嚴格小於資料包的pktuChainDataLength,這表示實際接收的資料比IP報頭中宣告的資料多。如果是真的,則進行修剪操作,要刪除額外的資料,如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


再者如果IP資料包的MF為1或者Fragment Offset大於0,則tfIpIncomingPacket()函式就要呼叫tfIpReassemblePacket()函式進行分片重組。如果IP分片資料接收不完整,則tfIpReassemblePacket()函式返回NULL。如果所有IP分片都到達並且沒有錯誤,則Treck協議棧使用pktuLinkNextPtr欄位將這些分片連結在一起,建立連結串列,並將包傳遞到下一層進行進一步處理,如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


最後如果已經收到完整的IP資料包,則tfIpIncomingPacket()函式根據IP資料包中的協議欄位的協議號,呼叫相應的協議包處理函式進行處理。在下列程式碼中,當協議號為UDP時,則呼叫tfUdpIncomingPacket()函式,當資料包協議為IP-in-IP協議(協議號4)時,會遞迴呼叫tfIpIncomingPacket()函式,程式碼實現如下所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


六、漏洞原理分析


1、CVE-2020-11896


前文已經介紹tfIpIncomingPacket()函式的實現過程,第二步的資料裁剪是漏洞的原因,如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

pktuLinkDataLength保留當前分片的大小,pktuChainDataLength保留整個IP資料包的大小。如果執行上述操作,將導致一個不一致性的狀態,其中pkt->pktuChainDataLength==pkt->pktuLinkDataLength,但可能有pkt->pktuLinkNextPtr指向其他分片。更進一步的其中連結串列上分片的總資料大小可能大於儲存在pktuChainDataLength變數中的大小。這種操作導致的不一致性將會導致後續報文處理髮生異常。


通過簡單地設定錯誤的IP包分片是無法觸發漏洞的,因為裁剪過後的分片資料在後續的tfIpReassemblePacket()函式操作中會根據pktuChainDataLength的大小,重新建立分片連結串列,不會造成不一致的狀態。理想的流程是先完成分片連結串列的建立,再進行連結串列資料總大小的裁剪流程,這樣就會進入不一致的狀態。

為了在IP層處理分片資料包並觸發執行有問題的流程程式碼,可以使用IP-in-IP資料包。對於分片的IP-in-IP資料包,tfIpIncomingPacket()函式將至少遞迴呼叫兩次,一次用於IP隧道包的內層IP資料包,多次用於外層IP資料包(每處理一個外層IP包分片算作一次)。


tfIpIncomingPacket()函式在處理IP隧道資料包的時候將內部IP資料包作為非分片資料包進行處理。內部資料包現在由多個分片組成,但在IP報頭中標記為非分片(MF=0),所以它不會再進入tfIpReassemblePacket()函式進行重組。它現在由一個連結串列中的幾個單獨的tsPacket連結組成,每個連結都有一個單獨的pktuLinkDataLength值。考慮下面的例子,它將有助於理解漏洞的成因:

●       Inner IP packet: IPv4{len=32, proto=17}/UDP{checksum=0, len=12},其中包括1000位元組的資料’A’。

●       Outer IP packet (fragment 1): IPv4{frag offset=0, MF=1, proto=4, id=0xabcd} ,其中包括40位元組的IP資料。

●        Outer IP packet (fragment 2): IPv4{frag offset=40, MF=0, proto=4, id=0xabcd} ,其中資料負荷為988位元組。


為了繞過UDP校驗,將校驗和欄位checksum設定為0。例項中的分片結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


當Treck協議棧處理外部分片時,它使用tsUserPacket結構中的pktuLinkNextPtr欄位來連結它們。如前所述,當tfIpIncomingPacket()函式處理內部IP資料包(協議為4,IP-in-IP)時,它已經完成了分片資料的重組(內部IP資料包由連結在一起的兩個tsPacket結構表示)。分片資料重組後的連結串列結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


由於tfIpIncomingPacket()函式在進行有效性判斷時,只考慮tsUserPacket中的pktuChainDataLength欄位(而不是pktuLinkDataLength),所以在處理內部IP包時將進入錯誤的連結串列長度的裁剪流程,從而導致了問題。


內部IP包通過了IP頭完整性檢查,在該例子中,內部IP包的總長度(32)小於連結串列資料長度(1000+8+20=1028),因此Treck協議棧將嘗試錯誤地修剪資料包,方法是將欄位pktuLinkDataLength和pktuChainDataLength設定為相同的值ipTotalLength(在我們的示例中為32)。這導致內部IP資料包由連結在一起的兩個tsPacket結構表示,但它們的資料總長度大於pktuChainDataLength欄位(修剪後pktuChainDataLength欄位不是1028位元組,而是等於32)。經過資料長度裁剪後的連結串列結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


現在已經使得連結串列達到了不一致的狀態,下面將介紹如何利用這種不一致的狀態來導致記憶體破壞。

在Treck協議棧程式碼中至少有一個程式碼路徑可以將分片資料複製到單個連續緩衝區中。具體的執行路徑為:tfUdpIncomingPacket() ->  tfSocketIncomingPacket() -> tfCopyPacket()。下面的程式碼是tfSocketIncomingPacket()函式處理UDP資料包的程式碼的一部分,如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


這段程式碼中tfSocketIncomingPacket()函式呼叫tfGetSharedBuffer()申請記憶體,其大小基於pktuChainDataLength欄位的值,然後通過tfCopyPacket()函式將資料包的不同分片逐個複製到新分配的記憶體空間中,發生溢位的程式碼如下所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

由於兩個分片中的pktuLinkDataLength之和為1000位元組,後續的tfCopyPacket函式將會把1000位元組的資料拷貝到這段記憶體中,這將導致堆溢位。


2、CVE-2020-11898


正如前文描述如何觸發CVE-2020-11896漏洞那樣,Treck TCP/IP協議棧無法正確處理通過IP-in-IP隧道傳入的IPv4分片。這也可能允許未經身份驗證的攻擊者從堆中洩漏記憶體。可以採用以下示例進行漏洞觸發:

●       內部IP資料包:IPv4 {ihl = 0xf,len = 100,proto = 0},有效載荷為'\ x00'* 40 +'\ x41'* 100。

●       外部IP資料包(分片1):IPv4 {frag offset = 0,MF = 1,proto = 4,id = 0xabcd},其中24個位元組來自內部IP資料包有效負載。這意味著將複製20個位元組的IP標頭,外加4個空位元組。

●       外部IP資料包(分片2):IPv4 {frag offset = 24,MF = 0,proto = 4,id = 0xabcd},來自內部IP資料包的其餘位元組作為有效負載。


例項中的分片結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


這裡ihl為0xf,表示為最大IP選項,長度為60位元組,資料包總長度total_length為100。當網路協議棧收到兩個分片時,它將使用tfIpReassemblePacket()函式重新組裝它們。分片資料重組後的連結串列結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


該tfIpReassemblePacket()函式使用tsUserPacket結構中的欄位pktuLinkNextPtr連結兩個分片。如果啟用了IP-in-IP隧道傳輸,則內部IP資料包將隨後由tfIpIncomingPacket()函式中處理,修剪後的pktuChainDataLength欄位不是160,而是等於100。經過資料長度裁剪後的連結串列結構如下圖所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


內部IP資料包通過IP標頭完整性檢查,因為僅考慮了tsUserPacket的pktuChainDataLength欄位(而不考慮pktuLinkDataLength)。因為在標準IP頭部(20個位元組)之後有4個空位元組,並且一個空位元組代表IP選項的末尾,IP選項解析通過檢查。由於內部IP資料包包含無效的IPv4協議編號(Protocol為0),進入default分支,然後直接進入TM_IP_LOCAL_FLAG分支。如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


因此網路協議棧將通過傳送型別為3(目標不可達)和程式碼為2(協議不可達)的ICMP錯誤訊息來拒絕該資料包。如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)



負責建立錯誤資料包的是tfIcmpErrPacket()函式。它分配一個新的資料包,初始化一些ICMP頭部欄位。如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


最後從後續資料包(內部IP資料包)中複製一些資料。複製部分如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


如程式碼所見,tfIcmpErrPacket()函式通過獲取IP報頭長度(以位元組為單位加上8,在實際情況下為60 + 8 = 68)與pktuLinkDataLength欄位(以及被裁剪為100)之間的最小值來計算要複製的位元組數 。由於傳送資料包的第一個分片的實際鏈路資料長度為24(而不是100),因此tfIcmpErrPacket()函式將從堆中複製68-24 = 44位元組的額外資料。然後設定v12_icmpErrPacket中相關資料。如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


最後呼叫tfIpSendPacket()函式傳送icmp_ErrPacket包到目標地址,這將導致44位元組的資訊洩露。


3、CVE-2020-11910

CVE-2020-11910是越界讀漏洞,該漏洞存在tfIcmpIncomingPacket函式中,該函式主要是處理ICMP包。tfIcmpIncomingPacket函式在處理裝置收到型別為3,code為4的ICMP包的時候,程式碼並沒有驗證後續資料的長度,直接就訪問了對應位置的資料,造成了越界讀漏洞。如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


4、CVE-2020-11911

CVE-2020-11911是未授權的敏感資訊更新漏洞,該漏洞存在tfIcmpIncomingPacket函式中,該函式主要是處理ICMP包。tfIcmpIncomingPacket函式在處理裝置收到型別為18(Address mask reply)包的時候,程式碼並沒有驗證裝置是否傳送過型別17(Address mask request)請求,就直接更新了裝置的子網掩碼。如下程式碼所示:

原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)


七、CVE-2020-11898漏洞驗證


遠端攻擊印表機,驗證視訊請訪問ADLab微信公眾號如下:

驗證視訊


八、參考


1、https://www.ietf.org/rfc/rfc2003.txt

2、https://www.ietf.org/rfc/rfc792.txt

3、https://www.ietf.org/rfc/rfc1853.txt

4、https://www.jsof-tech.com/ripple20/

5、https://kb.cert.org/vuls/id/257161

6、https://www.venustech.com.cn/article/1/11834.html

7、JSOF_Ripple20_Technical_Whitepaper_June20.pdf




原創 | Ripple20:Treck TCP/IP協議棧漏洞分析與驗證(附視訊)

相關文章