压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

Linux內核nftables子系統研究與漏洞分析

背景

近期,開源安全社區oss-security披露了多個Linux內核netfilter模塊相關漏洞,漏洞均出現在netfilter子系統nftables中,其中兩個漏洞在內核中存在多年,并且均可用于內核權限提升。漏洞編號分別為:CVE-2022-32250,該漏洞類型為釋放重引用;CVE-2022-1972,該漏洞類型為越界讀寫;CVE-2022-34918,該漏洞類型為堆溢出。

02?相關介紹

2.1 netfilter簡單介紹

netfilter是一個開源項目,用于執行數據包過濾,也就是Linux防火墻。這個項目經常被提到iptables,它是用于配置防火墻的用戶級應用程序。2014年,netfilter 防火墻添加了一個新子系統,稱為nftables,可以通過nftables用戶級應用程序進行配置。

2.2 nftables簡單介紹

nftables取代了流行的{ip,ip6,arp,eb}表。該軟件提供了一個新的內核數據包分類框架,該框架基于特定于網絡的虛擬機 (VM) 和新的nft用戶空間命令行工具。nftables重用了現有的netfilter子系統,例如現有的鉤子基礎設施、連接跟蹤系統、NAT、用戶空間隊列和日志子系統。對于nftables,只需要擴展expression即可,用戶自行編寫expression,然后讓nftables虛擬機執行它。nftables框架的數據結構如下所示:

Table{ Chain[ Rule (expression1,expression2,expression3,...)| ||--> expression_action ||--> expression_action |-->expression_action Rule (expression,expression,expression,...) ... ], Chain[ ... ], ...}

Table為chain的容器,chain為rule的容器,rule為expression的容器,expression響應action。構造成由table->chain->rule->expression四級組成的數據結構。

03?nftables子系統實現分析

通過發送netlink消息數據包來操作nftables,從netlink到nftables的調用過程如下所示:

在nfnetlink_rcv_batch()函數中對netlink消息進行操作。從netlink消息中剝離出nftable載荷,并依次進行對應處理。進入nfnetlink_rcv_batch()函數,首先根據subsys_id獲得nfnetlink_subsystem。

這里nftables類型為0xa。獲得subsystem后,然后拿到子系統對應的回調客戶端。通過nfnetlink_find_client()實現該功能。

對應nftables回調客戶端,在\\net\\netfilter\\nf_tables_api.c直接找到定義:

.cb數據域便是回調客戶端。可以看到針對不同的nftables操作,定義了多個回調客戶端,例如table的增刪改查操作。

然后再從netlink消息中剝離出netlink載荷,根據不同的消息類型進行不同的分發處理,消息類型如下所示:

依次調用nc->call_batch()進一步處理。

開始剝洋蔥式分析,第一層操作創建一個table,響應函數為nf_tables_newtable()。

先通過nla[NFTA_TABLE_NAME]來查找是否存在該table,如果存在,調用nf_tables_updtable(),如果不存在就創建該表。

創建完成后,然后就是必要的初始化操作。

初始化table->chains鏈表,table->sets鏈表,table->objects鏈表,table->flowtables鏈表。然后將table加到nftbales上下文中,最后將table鏈到net->nft.tables中。

第二步操作創建一個chain,響應函數為nf_tables_newchain()。首先先找table,無table直接退出。

找到table后,就找chain是否存在,存在進入update,不存在則添加一個新chain。

這里提供了兩種方式尋找chain,通過nla[NFTA_CHAIN_HANDLE]和nla[NFTA_CHAIN_NAME]進行尋找。未找到就調用nf_tables_addchain()創建之。

具體看該函數實現,首先分配一個chain,然后初始化chain->rules鏈表,并設置chain->hanle和chain->table。隨后進行初始化chain->name等操作,并將chain鏈到table->chains中。

第三步操作創建一個rule,響應函數為nf_tables_newrule(),首先相繼獲取table和chain,如果設置了nla[NFTA_RULE_EXPRESSIONS],會先把所有的expression遍歷出來,計算其總值放在size中。

如果設置了nla[NFTA_RULE_USERDATA],獲取userdata的大小放在usize中,最后分配內存,創建一個rule,隨即初始化相關數據域。

第四步操作創建expression,其實這一步和創建rule是連在一起的。都在nf_tables_newrule()函數中實現。expresssion總共有如下多種類型。

將用戶層傳進來的expression剝離出來后,依次放在rule中。

這里調用了nf_tables_newexpr()函數,會根據expression類型對其進行初始化。

以上就是一個完整的table->chain->rule->expression的創建過程。

04?相關漏洞分析

其實,回調客戶端中還提供了其他元素的創建操作,比如創建set(集合)。set只需要依附于table即可,可構成table->set->expression數據結構。

4.1 CVE-2022-32250

該漏洞是釋放重引用漏洞,出現在nf_tables_newset()函數中,該函數是創建一個set。set中也可以包含各種expression。首先看下nf_tables_newset()函數實現。

同樣地,先獲取table,再獲取set,如果set不存在就創建之。接下來根據set類型獲取對應的ops操作集,并確定set的大小,并分配之。

接下來,如果設置了nla[NFTA_SET_EXPR],并進入調用nft_set_elem_expr_alloc()函數進行處理。

進入該函數看具體實現。

行5128,首先進入nft_expr_init()函數分配一個expr,該函數具體實現如下所示。

行2686,調用kzalloc()分配一個expr,這是第一次操作,nft_expr結構體定義為:

data為動態數組,nft_expr本身是個不固定大小的結構體,使用時候才確定的大小,data處可以存放多種類型的expression,并匹配對應的ops操作集合。這里以nft_lookup為例子,nft_lookup結構體定義如下:

將expr合起拼接等于nft_lookup_expr,分配完expr后,就進入nf_tables_newexpr()函數,初始化expr(nft_lookup_expr),該函數實現如下所示:

行2652,調用對應的ops->init()函數進行初始化,具體看對應的nft_lookup_init()函數實現。

行64,首先獲得priv指針,即expr->data,也即是nft_lookup,進行一些初始化操作后,最后進行綁定操作。

行113,調用nf_tables_bind_set()進行綁定,具體看該函數實現。

行4490,宏list_add_tail_rcu將priv即nft_lookup綁定到set->binding中,即set->binding鏈表中引用了nft_lookup的地址,一路正常從nft_expr_init()函數返回后,具體看如下操作。

行5136,判斷expr->ops->type->flags是否為NFT_EXPR_STATEFUL,如果不是,那就直接跳到nft_expr_destory()函數進行釋放,具體看該函數實現。

行2727,首先調用nf_tables_expr_destroy()釋放set。

最后調用expr->ops->destroy()函數,這里對應函數是nft_lookup_destory()。

獲取priv,進一步調用nf_tables_destroy_set()函數,具體看該函數實現。

行4532,判斷set->bindings鏈表是否為空同時是否為匿名set,如果否,不釋放set并退出。

接下來回到nft_expr_destroy()函數中,釋放expr,這是第二步操作。這就出現了問題,expr被釋放了,但是set->bindings鏈表中卻引用了expr->nft_lookup->binding的地址指針,其調用流程如下所示:

nf_tables_newset nft_set_elem_expr_alloc nft_expr_init kzalloc 分配expr nf_tables_newexpr nft_lookup_init nf_tables_bind_set list_add_tail_rcu set->bindings引用expr->nft_lookup->binding nft_expr_destroy nf_tables_expr_destroy nft_lookup_destroy  nf_tables_destroy_set 未釋放set kfree 釋放expr

set->binding鏈表保存了對釋放后內存的指針引用,導致UAF。

4.2 CVE-2022-1972

該漏洞是一個越界讀寫漏洞,可以越界讀寫多個字節,繼續看nf_tables_newset()函數實現。

如果設置了nla[NFTA_SET_DESC],會調用nf_tables_set_desc_parse()函數解析nla[NFTA_SET_DESC]對應的數據。具體看該函數實現。

首先通過nla_parse_nested_deprecated()函數循環解析出數據中各種屬性標簽地址。如果設置了nla[NFTA_SET_DESC_CONCAT],進入nft_set_desc_concat()函數進行解析。

在nla_for_each_nested()循環中,依次遍歷出nlattar并調用nft_set_desc_concat_parse()函數處理,具體看該函數實現。

行4070,從nlattr中讀取len,行4075,將len寫入到desc->field_len數組中。該desc結構體定義如下:

其field_len數組大小為16,同時desc->field_count做自增操作,因此很容易發生溢出。如果這里溢出后,可以覆蓋到field_count,不過len最大為0x40。如果覆蓋到field_count后,返回到nf_tables_newset()函數中,后續操作如下:

會把set->field_count設置為desc.field_count,這個已經被覆蓋了,因此set->field_count也被修改了,然后就是循環將desc.field_len數組中數據寫到set->field_len數組中,看set結構體定義如下:

可以將desc->field_len數組中越界讀取的數據越界回寫到set->field_len數組后面的數據域中。這里可進行信息泄露獲得內核指針數據。通過nf_tables_fill_set()函數進行泄露,當用戶讀取set相關屬性時會調用該函數。兩種方式,較為方便一種:

將set->timeout,set->gc_init和set->policy返回給用戶。其desc定義在棧內存中,很容易越界讀取棧上數據,泄露棧數據。另一種方式為覆蓋set->udlen,它被覆蓋成一個較大的數值,然后通過set->udata讀取,這樣可以泄露更多數據。

4.3 CVE-2022-34918

該漏洞為堆溢出漏洞,出現在響應函數nf_tables_newsetelem()中,該函數部分實現如下:

首先判斷nla[NFTA_SET_ELEM_LIST_ELEMENTS]是否為空,然后根據table獲取set。行5622,通過nla_for_each_nested宏遍歷nla[NFTA_SET_ELEM_LIST_ELEMENTS]對應的數據,調用nft_add_set_elem()函數進行設置。該函數部分實現如下:

進一步剝離出nla[NFTA_SET_ELEM_DATA]對應的數據,調用nft_setelem_parse_data()函數進行處理,該函數實現如下:

調用nft_data_init()函數將data讀出來,同時回寫desc,其中包括desc->len。行4951,如果type不是NFT_DATA_VERDICT同時desc->len不等于set->dlen,釋放data并退出。如果將type設置為NFT_DATA_VERDICT,那么該判斷語句不會成立,并不會判斷desc->len和set->dlen是否相同,并成功返回。這就可以構造data_len不超過NFT_DATA_VALUE_MAXLEN,其為64,但是同時不等于set->dlen即可,在后續操作中就可以發生堆溢出。

這里說明一下set->dlen和desc->len的關系。在nf_table_newset()函數中,當設置了nla[NFTA_SET_DATA_TYPE]時,type不是NFT_DTAA_VERDICT并且nla[NFTA_SET_DATA_LEN]不為空時,會將nla[NFTA_SET_DATA_LEN]對應的數據賦值給desc.dlen。

后面初始化set時,并將desc.dlen賦值給set.dlen。

回到nft_add_set_elem()函數中,后續實現如下:

該拷貝的對應數據都拷貝完成后,調用nft_set_elem_init()函數進行初始化。具體看該函數實現。

行5159,調用kzalloc()函數分配內存,大小為elemsize+tmpl->len,這里的tmpl->len已經包括了desc.dlen。正常情況下desc.dlen應該是等于set->dlen的。行5170,如果存在nla[NFT_SET_EXT_DATA]時,并調用memcpy將data拷貝到elem中,長度為set->dlen,我們清楚set->dlen是不等于desc.dlen的,tmpl->len小了,因此發生溢出。

參考鏈接:

[1] https://blog.51cto.com/dog250/1583015

[2] https://www.openwall.com/lists/oss-security/2022/05/31/1

[3] https://www.openwall.com/lists/oss-security/2022/06/02/1

[4] https://www.openwall.com/lists/oss-security/2022/07/02/2

來源:ADLab

上一篇:容易被忽視的5個安全環節,比想象中的更危險!

下一篇:數字公平競爭新規則!歐盟《數字市場法》正式通過