【漏洞預警】Linux內核SCTP協議漏洞分析與復現

發布時間 2019-05-30

漏洞背景


Linux內核SCTP協議實現中存在一個安全漏洞CVE-2019-8956(CNVD-2019-06182、CNNVD-201902-823),可以導致拒絕服務。該漏洞存在于net/sctp/socket.c中的sctp_sendmsg()函數,該函數在處理SENDALL標志操作過程時存在use-after-free漏洞。


SCTP協議簡介


流控制傳輸協議(Stream Control Transmission Protocol,SCTP)是一種可靠的傳輸協議,它在兩個端點之間提供穩定、有序的數據傳遞服務(非常類似于 TCP),并且可以保護數據消息邊界(例如 UDP)。與TCP和 UDP不同,SCTP 是通過多宿主(Multi-homing)和多流(Multi-streaming)功能提供這些收益的,這兩種功能均可提高可用性。


多宿主(Multi-homing)為應用程序提供了比 TCP 更高的可用性。多宿主主機就是一臺具有多個網絡接口的主機,因此可以通過多個 IP 地址來訪問這臺主機。在 TCP 中,連接(connection) 是指兩個端點之間的一個通道(在這種情況下,就是兩臺主機的網絡接口之間的一個套接字)。SCTP 引入了“聯合(association)”的概念,它也是存在于兩臺主機之間,但可以使用每臺主機上的多個接口進行協作。



漏洞原理


漏洞補丁代碼如下,補丁代碼將list_for_each_entry換成了list_for_each_entry_safe。



宏定義list_for_each_entry功能是遍歷ep->asocs鏈表中的asoc節點。宏定義list_for_each_entry和list_for_each_entry_safe如下所示:



宏定義list_for_each_entry_safe中添加了一個n,該n用來存放pos指向的節點的下一個節點位置。使用該宏可以對鏈表進行刪除操作。


下面對sctp_sendmsg函數調用鏈進行分析。sctp_sendmsg是基于SCTP協議的sendmsg類型函數,用于發送SCTP數據包。關鍵實現如下:



行2038,從msg中解析出sinfo;行2043,獲取到sflags。



行2055,判斷sflags是否為SCTP_SENDALL。如果存在,進入list_for_each_entry循環中,依次遍歷ep->asocs鏈表。這里的asocs就是存放多個association連接的鏈表。SCTP_SENDALL標志代表向asocs鏈表中的所有association連接發送數據包。所以asocs鏈表中至少要存在一個association節點。進入sctp_sendmsg_check_sflags函數后,該函數實現如下:



首先,檢查asoc是否處于CLOSED狀態,檢查asoc是否處于監聽狀態,檢查asoc是否shutdown。



接下來,檢查sflags是否為SCTP_ABORT,根據rfc文檔可知ABORT的用法以及ABORT指令的數據包格式。SCTP_ABORT標志代表中止一個association連接,這個也是導致漏洞的關鍵。



行1863,sctp_make_abort_user構造ABORT指令的chunk;行1868,調用sctp_primitive_ABORT發送中止一個association的chunk。



通過調試可知調用sctp_sf_do_9_1_prm_abort函數進行ABORT操作,該函數將會進行如下操作:



添加一條刪除asoc的commands,然后返回SCTP_DISPOSITION_ABORT。正常返回,繼續分析,返回到sctp_do_sm函數中。



行1188正常返回后,行1191調用sctp_side_effects函數根據狀態機對應的狀態進行操作。



行1246,將asoc置空,ABORT標志代表中止一個association操作結束。從sctp_sendmsg_check_sflags函數返回到sctp_sendmsg函數中,宏list_for_each_entry循環中遍歷獲取第一個asoc節點時,進入sctp_sendmsg_check_sflags函數將第一個asoc置空,然后再進行遍歷后面節點時,就發生了零地址引用導致漏洞發生。


漏洞復現


將sflags設置成SENDALL | ABORT,保證進入list_for_each_entry循環和sctp_sendmsg_check_sflags()函數即可。在4.20內核下驗證如下。由于該漏洞是NULL-PTR deref,即是零地址解引用,無法進一步利用。



修復建議


該漏洞影響Linux Kernel 4.19.x和4.20.x,建議更新到version 4.20.8 或4.19.21。補丁鏈接如下:https://git.kernel.org/linus/ba59fb0273076637f0add4311faa990a5eec27c0