tft每日頭條

 > 生活

 > linux内核bug

linux内核bug

生活 更新时间:2024-09-28 15:23:00
一、漏洞詳情

近日,研究人員披露了一個Linux内核本地權限提升漏洞,發現在copy_page_to_iter_pipe和 push_pipe函數中,新分配的pipe_buffer結構體成員“flags”未被正确地初始化,可能包含舊值PIPE_BUF_FLAG_CAN_MERGE。攻擊者可利用此漏洞向由隻讀文件支持的頁面緩存中的頁面寫入數據,從而提升權限。該漏洞編号為CVE-2022-0847,因漏洞類型和“DirtyCow”(髒牛)類似,亦稱為“DirtyPipe”。

*嚴正聲明:本文僅限于技術讨論與分享,嚴禁用于非法途徑。

二、相關系統調用實現(一)pipe系統調用實現

調用pipe()創建一個管道,返回兩個文件描述符,fd[1]為讀,fd[2]為寫。這裡以linux-5.16.10内核代碼為例,調用到__do_pipe_flags()函數,該函數代碼實現如下:

linux内核bug(Linux内核權限提升漏洞)1

首先調用create_pipe_files(),然後調用get_unused_fd_flags()分别獲取未使用的文件描述符fdr和fdw,并寫入到指針fd中。create_pipe_files()函數調用get_pipe_inode()函數獲取一個inode,并初始化相關數據結構。get_pipe_inode()函數又調用alloc_pipe_info()函數分配一個pipe_inode_info,該結構體是一個内核pipe結構體,用于管道的管理和操作。具體看下alloc_pipe_info()函數,該函數實現代碼如下:

linux内核bug(Linux内核權限提升漏洞)2

【一>所有資源關注我,私信回複”資料“獲取<一】1、網絡安全學習路線2、電子書籍(白帽子)3、安全大廠内部視頻4、100份src文檔5、常見安全面試題6、ctf大賽經典題目解析7、全套工具包8、應急響應筆記

首先初始化pipe_bufs為PIPE_DEF_BUFFERS,該值為16,然後分配pipe,接着判斷pipe_bufs*PAGE_SIZE的大小,pipe_bufs最大值為128,最小值為2。

linux内核bug(Linux内核權限提升漏洞)3

然後開始分配pipe->bufs,正常一次性分配16個pipe_buffer,然後初始化pipe的相關成員,這裡并不會初始化pipe_bufs中的pipe_buffer。piper_buffer結構體定義如下:

linux内核bug(Linux内核權限提升漏洞)4

其中page用于存放數據,大小為一個頁面,ops為對應内存頁面操作集,成員flags為buffer類型。這16個pipe_buffer構成一個管道緩沖區的循環數組,pipe->head指向緩沖區生産點,pipe->tail指向消費點,在pipe的管理下,循環地用于數據的讀取和寫入。

當向管道中寫入數據時,會調用pipe_write()函數,該函數部分實現代碼如下:

linux内核bug(Linux内核權限提升漏洞)5

首先從pipe->head開始,判斷pipe是否為滿的。不滿的情況下,拿出一個pipe_buffer,判斷page是否已分配,未分配随即分配一個新page,然後初始化這個pipe_buffer相關成員,實現代碼如下:

linux内核bug(Linux内核權限提升漏洞)6

行527,将buf->flags設置為PIPE_BUF_FLAG_CAN_MERGE,表示該buffer是可以合并的。最後調用copy_page_from_iter()函數将數據拷貝到新分配的page中。當從管道中讀取數據時,就是逆過程,其間并不改變既定buffer的頁面類型,不再贅述。

(二)splice系統調用實現

splice是Linux 2.6.17新加入的系統調用,用于在兩個文件間移動數據,而無需内核态和用戶态的内存拷貝,但需要借助管道(pipe)實現。大概原理就是通過pipe buffer實現一組内核内存頁(pages of kernel memory)的引用計數指針(reference-counted pointers),數據拷貝過程中并不真正拷貝數據,而是創建一個新的指向内存頁的指針。也就是說拷貝過程實質是指針的拷貝,稱為零拷貝技術。

調用splice系統調用時,内核中會調用do_splice()函數,該函數實現代碼如下:

linux内核bug(Linux内核權限提升漏洞)7

分三種情況,第一種為in/out均為pipe類型,第二種是in為pipe類型,第三種是out為pipe類型,這裡我們分析第三種情況。調用spilce_file_tp_pipe()函數将數據寫入pipe中,具體會調用到generic_file_splice_read()函數,這裡以linux-2.6.17内核版本為例,更容易理解零拷貝過程。該函數實現如下:

linux内核bug(Linux内核權限提升漏洞)8

然後調用到__generic_file_splice_read()函數,該函數實現代碼如下:

linux内核bug(Linux内核權限提升漏洞)9

首先獲取in->f_mapping,該結構體是用于管理文件(struct inode)映射到内存的頁面(struct page)的,其實就是每個file都有這麼一個結構,将文件系統中這個file對應的數據與這個file對應的内存綁定到一起。然後定義一個splice_pipe_desc結構體,該結構體用于中轉file對應的内存頁。接下來就是将file對應的内存頁面整理放在spd中,過程比較複雜,略過。最後調用splice_to_pipe()函數操作pipe和spd,該函數實現關鍵代碼如下所示:

linux内核bug(Linux内核權限提升漏洞)10

依次循環地從spd->pages中取出内存頁放在對應的buf->page中。可以看出這裡僅僅是對内存頁面進行轉移,而沒有進行任何内存拷貝。

三、漏洞原理與補丁(一)漏洞原理

在linux-5.16.10内核中,調用splice()函數将數據寫入管道時,調用路徑如下所示:

linux内核bug(Linux内核權限提升漏洞)11

比linux-2.6.17内核版本的複雜,最終會調用copy_page_to_iter_pipe()函數操作内存頁面,該函數實現代碼如下:

linux内核bug(Linux内核權限提升漏洞)12

如前文所述,從pipe中取出buf,隻是替換了ops,page,offset和len,并沒有修改buf->flags,因此該buffer所包含的頁面是可以合并的。當再次向管道中寫入數據時,因為pipe非初次使用,首先判斷要寫入的buffer類型,如果buf->flags為PIPE_BUF_FLAG_CAN_MERGE,行466,直接調用copy_page_from_iter()函數進行内存拷貝,而目的地址為buf->page,這個buf->page實際上就是來自file中對應的内存頁面。

linux内核bug(Linux内核權限提升漏洞)13

(二)補丁

該漏洞補丁在copy_page_to_iter_pipe()函數和push_pipe()函數中,将buf->flags置零。其中push_pipe()函數可在其他路徑中觸發,不再贅述。

linux内核bug(Linux内核權限提升漏洞)14

四、利用分析

首先,調用pipe創建管道并通過寫讀操作将管道中的buffer類型設置為PIPE_BUF_FLAG_CAN_MERGE。

linux内核bug(Linux内核權限提升漏洞)15

然後将要覆蓋的文件通過splice寫入到pipe中,公開的利用中被覆蓋的文件為/usr/bin/pkexec,因為該程序具備suid能力。

linux内核bug(Linux内核權限提升漏洞)16

觸發漏洞後,此時pipe中buf所包含的内存頁面均是指向/usr/bin/pkexec文件所屬的内存頁面,而且内存頁面都是可以合并的。最後再次調用write()函數将提權payload寫入pipe中,即寫入/usr/bin/pkexec文件中,然後運行/usr/bin/pkexec提升權限。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved