您好,登錄后才能下訂單哦!
本篇內容介紹了“如何理解Linux X.25套接字棧越界讀寫漏洞”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
X.25接口協議于1976年首次提出,它是在加拿大DATAPAC公用分組網相關標準的基礎上制定的,在1980年、1984年、1988年和1993年又進行了多次修改,是目前使用最廣泛的分組交換協議。X.25協議是數據終端設備(DTE)和數據電路終接設備(DCE)之間的接口協議,該協議的制定實現了接口協議的標準化,使得各種DTE能夠自由連接到各種分組交換網上。作為用戶設備和網絡之間的接口協議,X.25協議主要定義了數據傳輸通路的建立、保持和釋放過程所需遵循的標準,數據傳輸過程中進行差錯控制和流量控制的機制以及提供的基本業務和可選業務等。
X.25協議采用分層的體系結構,自下而上分為三層:物理層、數據鏈路層和分組層,分別對應于OSI參考模型的下三層。各層在功能上相互獨立,每一層接受下一層提供的服務,同時也為上一層提供服務,相鄰層之間通過原語進行通信。在接口的對等層之間通過對等層之間的通信協議進行信息交換的協商、控制和信息的傳輸。
1996/12/18,Linux 內核發布了 2.1.16版,第一次引入了對X.25協議的支持,定義了 AF_NFC 地址族:
#define AF_X25 9 /* Reserved for X.25 project */
X25 sockets 為 X.25 數據包層協議(packet layer protocol)提供接口。 應用程序可以使用標準的 ITU X.25 建議 (X.25 DTE-DCE 模式)在公共 X.25 數據網中進行通訊。AF_X25 socket 地址族用 struct sockaddr_x25 代表 ITU-T X.121 規范中定義的網絡地址。
struct x25_address { char x25_addr[16]; }; struct sockaddr_x25 { sa_family_t sx25_family; /* 必須是 AF_X25 */ x25_address sx25_addr; /* X.121 地址 */ };
sx25_addr 包含一個空零結尾的字符串 x25_addr[] 。 sx25_addr.x25_addr[] 由最多 15 個 ASCII字符(不包括結束的 0)構成 X.121 地址。 只能使用數字 `0' 到 `9' 。
X.25套接字僅支持 SOCK_SEQPACKET 類型,在建立X.25套接字時, socket()調用的參數如下
x25_socket = socket(PF_X25, SOCK_SEQPACKET, 0);
Linux內核中對應的 struct proto 和 struct proto_ops:
static struct proto x25_proto = { .name = "X25", .owner = THIS_MODULE, .obj_size = sizeof(struct x25_sock), }; static const struct proto_ops x25_proto_ops = { .family = AF_X25, .owner = THIS_MODULE, .release = x25_release, .bind = x25_bind, .connect = x25_connect, .socketpair = sock_no_socketpair, .accept = x25_accept, .getname = x25_getname, .poll = datagram_poll, .ioctl = x25_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_x25_ioctl, #endif .gettstamp = sock_gettstamp, .listen = x25_listen, .shutdown = sock_no_shutdown, .setsockopt = x25_setsockopt, .getsockopt = x25_getsockopt, .sendmsg = x25_sendmsg, .recvmsg = x25_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, };
該漏洞位于x25_bind函數中,以Linux內核最新的穩定版本5.9.8為例,https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/x25/af_x25.c?h=v5.9.8#n677
677 static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) 678 { 679 struct sock *sk = sock->sk; 680 struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; ………………………………………………………………………… 692 len = strlen(addr->sx25_addr.x25_addr); 693 for (i = 0; i < len; i++) { 694 if (!isdigit(addr->sx25_addr.x25_addr[i])) { 695 rc = -EINVAL; 696 goto out; 697 } 698 } …………………………………………………………………………
x25_bind有3個參數,第二個參數uaddr是應用層傳遞過來的套接字地址
在680行,uaddr轉成了X.25套接字地址指針
在692行,調用strlen函數,獲取addr->sx25_addr.x25_addr的長度
從693行開始的for循環,依次判斷addr->sx25_addr.x25_addr字符串里面是不是全部是數字。按照ITU-T X.121 規范,套接字地址只能使用數字 `0' 到 `9' 數字表示,不能用其他字符。
再來看一下sockaddr_x25 結構體定義:
struct x25_address { char x25_addr[16]; }; struct sockaddr_x25 { sa_family_t sx25_family; /* 必須是 AF_X25 */ x25_address sx25_addr; /* X.121 地址 */ };
X.121 地址對應的結構體struct x25_address,其實是一個ascii字符串數組,大小是16。
X.121 地址最多15個ascii字符,而struct x25_address里面的x25_addr字符串數組大小是16,所以最后面是用來存儲字符串末尾的空字符的。
x25_bind函數的漏洞在于:調用strlen函數之前,沒有判斷struct x25_address里面的x25_addr字符串是不是以空字符結尾的。如果末尾不是空字符,那么strlen函數獲得的長度將會大于16,在接下來的for循環中,將會越界讀取addr正常范圍之后的數據。
該漏洞由多個X.25套接字相關漏洞組合而成,仍然以Linux內核最新的穩定版本5.9.8為例,從x25_connect函數開始,源代碼見:https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/x25/af_x25.c?h=v5.9.8#n744
744 static int x25_connect(struct socket *sock, struct sockaddr *uaddr, 745 int addr_len, int flags) 746 { 747 struct sock *sk = sock->sk; 748 struct x25_sock *x25 = x25_sk(sk); 749 struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; ………………………………………………………………………… 803 x25->dest_addr = addr->sx25_addr; ………………………………………………………………………… 811 x25_write_internal(sk, X25_CALL_REQUEST);
x25_connect有4個參數,第二個參數uaddr是應用層傳遞過來的套接字地址
在749行,uaddr轉成了X.25套接字地址指針
在803行,addr->sx25_addr 賦給了x25套接字的dest_addr,用作連接的目標地址,
在811行,調用了x25_write_internal函數
x25_write_internal函數源代碼見:https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/x25/x25_subr.c?h=v5.9.8#n109
109 void x25_write_internal(struct sock *sk, int frametype) 110 { 111 struct x25_sock *x25 = x25_sk(sk); ………………………………………………………………………… 115 unsigned char addresses[1 + X25_ADDR_LEN]; ………………………………………………………………………… 179 switch (frametype) { 180 181 case X25_CALL_REQUEST: 182 dptr = skb_put(skb, 1); 183 *dptr++ = X25_CALL_REQUEST; 184 len = x25_addr_aton(addresses, &x25->dest_addr, 185 &x25->source_addr); …………………………………………………………………………
111行,sk套接字轉成x25套接字指針
115行,在棧上聲明了一個字符串數組addresses,長度是1 + X25_ADDR_LEN,也就是17
傳遞過來的x25_write_internal函數第二個參數frametype是X25_CALL_REQUEST,由此在第181行開始處理
184行,調用x25_addr_aton函數,傳遞了在棧上聲明的字符串數組addresses,x25套接字的dest_addr和source_addr,
dest_addr是在x25_connect函數中賦值的
source_addr是在x25_bind函數中賦值的
繼續看x25_addr_aton源代碼:https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/x25/af_x25.c?h=v5.9.8#n154
154 int x25_addr_aton(unsigned char *p, struct x25_address *called_addr, 155 struct x25_address *calling_addr) 156 { 157 unsigned int called_len, calling_len; 158 char *called, *calling; 159 int i; 160 161 called = called_addr->x25_addr; 162 calling = calling_addr->x25_addr; 163 164 called_len = strlen(called); 165 calling_len = strlen(calling); 166 167 *p++ = (calling_len << 4) | (called_len << 0); 168 169 for (i = 0; i < (called_len + calling_len); i++) { 170 if (i < called_len) { 171 if (i % 2 != 0) { 172 *p |= (*called++ - '0') << 0; 173 p++; 174 } else { 175 *p = 0x00; 176 *p |= (*called++ - '0') << 4; 177 } 178 } else { 179 if (i % 2 != 0) { 180 *p |= (*calling++ - '0') << 0; 181 p++; 182 } else { 183 *p = 0x00; 184 *p |= (*calling++ - '0') << 4; 185 } 186 } 187 } 188 189 return 1 + (called_len + calling_len + 1) / 2; 190 }
x25_addr_aton函數有三個參數:
unsigned char *p:在棧上聲明的字符串數組addresses
struct x25_address *called_addr:x25_connect函數中賦值的目標地址
struct x25_address *calling_addr:x25_bind函數中賦值的當前地址
164、165行,調用strlen獲取目標地址、當前地址的長度,
169行開始的for循環,用得到的兩個地址的長度,不斷地往參數p指向的內存中寫值
和x25_bind函數一樣,x25_connect在賦值時,也沒有判斷應用層傳遞過來的套接字地址是不是以空字符結尾,在整個的傳遞過程中,x25_write_internal、x25_addr_aton兩個函數也沒有判斷。
由此顯而易見的是,如果x25_bind、x25_connect兩個函數賦值的地址不是以空字符結尾的話,那么x25_addr_aton函數調用的兩個strlen獲取到的長度都將超過16,在169行for循環寫入addresses時,將造成addresses的溢出,造成嚴重的棧溢出漏洞。
最初的2.1.16版本的x25_bind函數(見https://elixir.bootlin.com/linux/2.1.16/source/net/x25/af_x25.c#L697),沒有對addr做任何判斷,直接賦給了sk套接字,所以上述的越界讀漏洞在最初的版本中并不存在。當然,按照ITU-T X.121 規范,套接字地址只能使用數字 `0' 到 `9' 數字表示,不能用其他字符,所以這里的功能其實是不正常的。
從2.6.34版本開始(2010/05/16發布),x25_bind函數中開始加入strlen函數和for循環,用于判斷addr的有效性,并持續至現在的最新版本5.9.8.所以上述越界讀漏洞影響linux內核版本為:2.6.34~5.9.8
x25_connect、x25_write_internal、x25_addr_aton三個函數,自從引入以來,對dest_addr的沒有變化,一直都沒有判斷是否以空字符結尾,所以上述越界寫漏洞影響linux內核版本為:2.1.16~5.9.8,持續時間長達24年。
“如何理解Linux X.25套接字棧越界讀寫漏洞”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。