概述
在Linux內核處理TCP網絡數據包時候存在缺陷導致三個漏洞,CVE編號分別為:CVE-2019-11477,CVE-2019-11478和CVE-2019-11479。最嚴重的漏洞CVE-2019-11477,可以讓遠程攻擊者DDOS系統導致內核崩潰,從而影響系統的可用性。根據Redhat官方安全中心標準CVE-2019-11477為嚴重漏洞(Important),而CVE-2019-11478和CVE-2019-11479中等漏洞(Moderate)。
三個漏洞中前兩個與數據包選擇性確認(SACK)和最大段大小(MSS)相關,第三個漏洞與最大段大小(MSS)相關。
Redhat官方目前已經提供升級包和緩解參數設置措施,。
漏洞背景
Linux內核處理TCP網絡數據包時候存在缺陷導致三個漏洞,CVE編號分別為:CVE-2019-11477,CVE-2019-11478和CVE-2019-11479。漏洞僅可以用於DOS拒絕攻擊,不涉及信息洩露或者權限提升。
選擇性確認SACK
TCP的選擇性確認(Selective Acknowledgment,SACK)是一種機制,數據接收方可以通知發送方有關成功接受的數據包段的信息。這樣來通知發送方重傳發送包集中丟失的數據包段。禁用TCP SACK時,需要重傳整個數據包完成數據傳輸。
最大數據包段大小MSS
最大數據包段大小(maximum segment size,MSS)是TCP報頭中設置的參數,用來指定重建TCP數據包段需要的數據的大小。
由於數據包在變化的路由網絡傳輸過程中可能會碎片化,因此主機必須將MSS指定為等於主機可以處理的最大IP數據報文的有效負載大小。MSS過大可能會導致數據包流在到達目的地的過程中碎片化,較小的數據包可以確保較少的碎片但最終會導致未使用的開銷。
作業系統和傳輸類型默認設置了MSS大小。有特權訪問權限的攻擊者可以在數據包中創建特殊的MSS設置的原始數據包發送攻擊。
TCP SACK
TCP是面向連接的協議。當雙方希望通過TCP連接進行通信時,他們通過TCP握手交換某些信息建立連接,例如發起一個TCP請求,通過SYN發送初始序列ID,確認ID,連接使用的最大數據包段大小(MSS),認證信息和處理選擇性確認(SACK)等。整體TCP連接通過我們熟知的三次握手最終建立。
TCP通過一個數據段單元發送和接收用戶數據包。 TCP數據段由TCP頭,選項和用戶數據組成。每個TCP段都有序列號(SEQ)和確認號(ACK)。
接收方通過SEQ號和ACK號來跟蹤成功接收了哪些段。ACK號下一個預期接受的段。
上圖中用戶A通過13個100位元組的段發送1k字節的數據,每個段具有20位元組的TCP頭,總計是13個段。在接收端,用戶B接收了段1,2,4,6,8-13,而段3,5和7丟失,B沒有接收到。
通過使用ACK號,用戶B告訴A,他需要段3,用戶A收到B接收到2,而沒有收到3,A將重新發送全部段,儘管B已經收到了4,6和8-13段。所以導致大量重複傳輸,性能低下。
選擇性確認SACK
為了解決上面提到的問題問題,Linux在RFC-201議案中提出了選擇性確認(Selective Acknowledgement,SACK)機制。通過SACK,B可以使用TCP選項欄位通知A已成功接收了的所有段(1,2,4,6,8-13),A只需重傳段3,5和7就可以。這樣一來可以大大節省了網絡帶寬,提高了性能。
SACK奔潰: CVE-2019-11477
套接字緩衝區(SKB):
套接字緩衝區(Socket Buffer,SKB)是Linux TCP/IP實現中使用的核心的數據結構。它是緩衝區的連結列表,用來保存網絡數據包。該列表可以用做傳輸隊列,接收隊列和SACK隊列,重傳隊列等。SKB可以將分組數據保存成片段,Linux SKB最多可以容納17個片段。該數在linux/include/linux/skbuff.h定義:
define MAX_SKB_FRAGS (65536/PAGE_SIZE + 1) => 17
在x86平臺上每段數據包中最多可容納32KB的數據(PowerPC上為64KB)。數據包將發送時,它被放置在發送隊列中,它的詳細信息定義控制緩衝區結構中( linux/include/linux/skbuff.h):
其中,tcp_gso_segs和tcp_gso_size欄位用通知設備驅動程序有關分段卸載的信息。
當Segmentation offload選項打開並且SACK機制也啟用時,由於數據包丟失和某些數據包的選擇性重傳,SKB可能最終持有多個數據包,由tcp_gso_segs計數。列表中的多個這樣的SKB被合併為一個以有效地處理的不同SACK塊。它涉及將數據從一個SKB移動到列表中的另一個SKB。在此數據移動期間,SKB結構可以達到其最大限制為17個片段,並且tcp_gso_segs參數可以溢出並觸發下面的BUG_ON()調用,從而導致所述內核奔潰。
遠程用戶可以通過將TCP連接的最大段大小(MSS)設置為48位元組的最低限制並發送一系列特殊的SACK數據包來觸發此問題,導致最低MSS每段僅留下8位元組數據,從而增加了發送所有數據所需的TCP段數。
影響版本
影響Linux 內核2.6.29及以上版本
Centos
修復和緩解方案
及時更新補丁
可以等待發行方的補丁包,然後通過包管理器(yum,apt)等升級。
內核設置
通過設置Linux內核參數禁用SACK,方法:
echo 0 > /proc/sys/net/ipv4/tcp_sack
後者
sysctl -w net.ipv4.tcp_sack=0
可以防止CVE-2019-11477 和 CVE-2019-11478攻擊。
可以防火牆設置限制MSS大小,對太小的包直接DROP:
firewall:
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --tcp-flags SYN SYN -m tcpmss --mss 1:500 -j DROP
firewall-cmd --permanent --direct --add-rule ipv6 filter INPUT 0 -p tcp --tcp-flags SYN SYN -m tcpmss --mss 1:500 -j DROP
firewall-cmd -reload
firewall-cmd --permanent --direct --get-all-rules
iptables:
iptables -I INPUT -p tcp --tcp-flags SYN SYN -m tcpmss --mss 1:500 -j DROP
iptables -nL -v
批量更新(Ansible)
最後提供一個Ansible cook文件用來批量更新禁止SACK:
將以上代碼保存為disable_tcpsack.yml,然後ansible執行:
ansible-playbook -e HOSTS=all disable_tcpsack.yml