本文是翻譯文章,文章原作者trendmicro,文章來源:blog.trendmicro.com
原文地址:https://blog.trendmicro.com/trendlabs-security-intelligence/cve-2019-11815-a-cautionary-tale-about-cvss-scores/
譯文僅供參考,具體內容表達以及含義原文為準
Linux內核中的漏洞並不罕見。現在Linux已經大約有2600萬行代碼,僅在2018年就添加了3,385,121行代碼,移除了2,512,040行代碼,。在這種量級的代碼複雜度下很難不存在漏洞。然而有些漏洞還是比較少見,比如未授權遠程代碼執行(RCE)漏洞:這是每個系統管理員都不想看到的關鍵問題。
2019年5月8日,美國國家漏洞資料庫(NVD)公布了Linux內核漏洞(CVE-2019-11815)的詳細細節,通用漏洞評分系統(CVSS)3.0的基本評分為8.1。漏洞細節包括:可以通過「網絡」進行攻擊、無需特權、能獲得管理員級別的代碼執行,該漏洞在機密性、完整性和可用性(CIA)方面影響程度都「較高」。如果只看該評分,這似乎是非常嚴重的問題。然而在評估漏洞影響範圍方面,我們不應當只參考CVSS基礎評分所給出攻擊方式、權限級別以及CIA標準。
CVSS 3基礎評分的一個因素就是攻擊複雜度,該漏洞在這方面也獲得了「高」評級。這意味著如果想成功利用漏洞,攻擊者需要依賴一些難以滿足的特定條件。根據CVSS 3.0標準,該評級意味著「漏洞成功利用依賴於攻擊者無法控制的一些條件」以及「漏洞無法隨意利用,需要攻擊者針對漏洞組件在前期準備和執行方面投入較多精力,才可能成功攻擊」。
詳細查看漏洞細節後,我們就可以知道為什麼這個評分在技術層面上是正確的(特別是要考慮到攻擊複雜度方面因素),但並不能完全代表企業和用戶所面臨的實際風險。
根據NVD的描述,該漏洞存在於「內核版本低於5.0.8之前的Linux發行版,具體位於net/rds/tcp.c的rds_tcp_kill_sock函數中」,並且存在「競爭條件,可以導致uaf(釋放後使用)問題,與net命名空間清理有關」。從代碼角度而言,這個漏洞描述非常準確且簡潔,但由於提到TCP這個詞,並且缺乏關鍵信息,這可能容易因此不必要的警報。
該漏洞涉及到的第一個組件是RDS(Reliable Datagram Sockets),這是由Oracle開發的一個套接字接口和協議,方便單個套接字向大量不同的端點發送和接受數據。當TCP作為底層傳輸協議時,該漏洞就涉及到RDS:RDS頭中的應用數據被封裝後通過TCP傳輸,通常會發送到16385埠,然後解封裝並傳輸至RDS套接字。
除了Oracle的官方文檔和維基百科上非常簡短的一個頁面之外,我們找不到關於RDS和典型用法的太多資料。由於該協議存在模糊性,並且之前還存在本地提權漏洞,因此多年來大多數Linux發行版(如Ubuntu)已經將與RDS有關的內核模塊列入黑名單中,這樣也迅速將這類漏洞的潛在風險控制在一定範圍內。
那麼當rds及rds_tcp內核模塊處於啟用狀態會出現什麼情況呢?
當在TCP協議上使用RDS時,底層TCP傳輸完全由內核來控制。這意味著當客戶端建立一個新的RDS套接字時,內核就會在tcp_connect.c中通過rds_tcp_conn_path_connect()打開TCP套接字,而該函數由threads.c中的rds_connect_worker()線程函數負責調用。
圖1. threads.c中的rds_connect_worker()調用rds_tcp_conn_path_connect()
當客戶端的底層TCP套接字持續無法連接時,就會出現與RDS有關的問題。當TCP的connect()失敗時,系統就會調用rds_tcp_restore_callbacks()函數,將rds_tcp_connection結構中的t_sock指針設置為NULL,這是非常合理的行為。
圖2. rds_tcp_conn_path_connect()調用rds_tcp_restore_callbacks()
圖3. 在rds_tcp_restore_callbacks()中將t_sock設置為NULL
當引入第二個漏洞組件時就會出現問題:網絡命名空間(network namespaces)。網絡命名空間可以為指定的命名空間使用獨立的一組接口和路由表,其中傳統意義上整個作業系統與其他每個進程共享相同的接口和路由表。Docker等平臺會使用這個命名空間功能為容器提供網絡隔離機制。
當rds_tcp_init()初始化RDS-TCP套接字時,就會調用網絡命名空間函數register_pernet_device(),傳入指向pernet_operations結構體的一個指針(rds_tcp_net_ops),其中包含待執行的初始化和退出函數,當初始化或刪除網絡命名空間以及激活套接字時就會調用這些函數。
圖4. 調用register_pernet_device()註冊網絡命名空間設備
圖5. rds_tcp_exit_net()為網絡命名空間設備的退出函數
退出函數rds_tcp_exit_net()會調用rds_tcp_kill_sock(),該函數用來清理RDS-TCP套接字中涉及的各個部分。其中有個清理過程會創建待清理的連接列表:tmp_list。
內核會檢查每個連接,判斷在用的TCP套接字對應的t_sock指針是否為NULL,如果滿足條件,就不會將t_tcp_node添加到「清理列表」中。這樣處理的結果就是,內核不會針對這些節點調用rds_conn_destroy(),也不會執行許多「清理」操作。
圖6. 如果t_sock為NULL則rds_tcp_kill_sock()不會執行清理操作
最重要的是,rds_connect_worker()線程不會停止,會繼續嘗試重新連接。最終,作為清理命名空間的一個環節,底層網絡結構會被釋放,而正在運行的rds_connect_worker()可能還會用到該結構,這樣就會觸發釋放後重用(use-after-free)問題。從技術角度上來講,這個缺陷可以描述為:無需特殊權限,如果該漏洞成功被利用就可以實現管理員級別的代碼執行。
這個問題修復起來也非常簡單:系統管理員只需要確保存在漏洞的模塊已被禁用,或者已安裝了新版內核。
分析完CVE-2019-11815後,該漏洞對用戶而言意味著什麼?潛在的受害者首先需要加載通常處於黑名單中的rds及rds_tcp模塊,如果這些模塊沒被加載,那就不需要後續操作。如果攻擊者終於找到了極其罕見的目標,由於TCP的connect()操作只由RDS-TCP客戶端發起,跟服務端無關,因此攻擊者需要誘導目標從網絡命名空間中連接到攻擊者可控的RDS-TCP套接字。
攻擊者下一個任務就是觸發底層TCP連接失敗,與此同時觸發目標用戶的網絡命名空間被系統清理:這是遠程攻擊者幾乎無法完成的任務。除此之外,競爭條件(利用非預期時間點觸發的缺陷來影響其他操作)通常也非常難以利用,需要大量嘗試才有可能成功。
將這些條件考慮在內後,我們可以認為該漏洞「未經身份驗證被遠程利用」的概率近乎為零。該漏洞被用來提升本地權限的概率也非常低,需要系統加載通常處於黑名單中的rds及rds_tcp模塊。
雖然從技術角度上來看這個漏洞CVSS評分沒有問題,但用戶應該了解到該風險同樣依賴於攻擊成功的可能性,因為攻擊者能否成功實際上受利用複雜度和條件所限。在實際環境中這個漏洞很難利用成功,絕大多數Linux伺服器在遠程環境中根本不受影響。