Netfilter nf_tables在处理批量请求时存在使用后释放漏洞 CVE-2023-32233

作者:Sec-Labs | 发布时间:

项目地址

https://github.com/oferchen/POC-CVE-2023-32233

小编推荐

该项目是一个漏洞利用工具,用于利用Netfilter nf_tables组件中的Use-After-Free漏洞,通过修改 nft_set 中已释放的内存块,并在后台工作线程中调用 nf_tables_set_elem_destroy() 时利用该漏洞,进行任意内存读写,修改系统进程的路径,以获取root权限。

相关技术点

  • Use-After-Free漏洞
  • Netfilter nf_tables组件
  • Linux内核基础知识
  • 基于大量元素的nft_set
  • 基于元素大小的nft_set
  • 内核地址空间布局随机化(KASLR)
  • 控制流完整性(CFI)

项目用途

该项目的主要用途是漏洞利用,通过利用Netfilter nf_tables组件中的Use-After-Free漏洞,进行任意内存读写,修改系统进程的路径,以获取root权限。 通过该工具,可以对系统进行渗透测试,也可以进行安全研究和学习。

处理批量请求时Netfilter nf_tables中的Use-After-Free漏洞

演示

84f2df8a82164130

 

 

漏洞详情

受影响的代码源自于来自https://kernel.org/的官方Linux内核,并且是Netfilter nf_tables组件的一部分(net/netfilter/nf_tables_api.c)。

Netfilter nf_tables允许将其配置更新为原子操作。当使用此功能时,用户模式客户端发送包含基本操作列表的批量请求。然后,Netfilter nf_tables将批处理中的所有操作作为单个事务进行处理。在处理批处理时,Netfilter nf_tables然后检查配置状态更新以确保每个连续的基本操作都是有效的,并且还考虑了批处理中所有先前操作的状态更新。但是,当前实现的检查是不充分的。

在我们具体的场景中,我们从具有匿名nft_set上的lookup表达式的Netfilter nf_tables配置开始,并且匿名nft_set包含一些元素。接下来,我们发送一个包含以下两个基本操作的批处理请求:

  1. NFT_MSG_DELRULE操作以删除nft_rule。 请注意,这也隐式删除了lookup表达式和匿名nft_set。
  2. NFT_MSG_DELSETELEM操作以删除已删除的匿名nft_set的任何元素。

Netfilter nf_tables的当前版本接受上述批处理请求。然后,它调用nf_tables_commit_release()将已释放的资源附加到nf_tables_destroy_list中。然后,nf_tables_destroy_list由nf_tables_trans_destroy_work()处理,它首先通过调用以下内容来释放与NFT_MSG_DELRULE操作相关的资源:

nft_commit_release()
    nf_tables_rule_destroy()
        nf_tables_expr_destroy()
            expr->ops->destroy()指向nft_lookup_destroy()
                nf_tables_destroy_set()
                    nft_set_destroy()
                        kvfree()释放由nft_set使用的内存

在处理NFT_MSG_DELSETELEM操作之前,其中引用已释放的nft_set通过nft_trans_elem_set()进行访问,此时后续调用如下:

nft_commit_release()
    nf_tables_set_elem_destroy()
        nft_set_elem_ext()

在上面的nft_set_elem_ext()中,访问已释放的nft_set的内存位置以确定nft_set_ext的位置:

static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
                                                   void *elem)
{
        return elem + set->ops->elemsize;
}

用于接下来的操作。因此,每当set->ops->elemsize的值被破坏时,某些意外的内存位置可能被解释为要销毁的nft_expr列表:

static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
                                       const struct nft_set *set, void *elem)
{
        struct nft_set_ext *ext = nft_set_elem_ext(set, elem);

        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
                nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext));

利用技巧

利用上述漏洞需要在nf_tables_trans_destroy_work()中赢得竞争,该函数从Linux内核的后台工作线程执行。这似乎使实际利用变得复杂,甚至在考虑现有的缓解措施(例如内核slab分配器的加固,内核地址空间布局随机化(KASLR)以及尤其是控制流完整性)之前。但是,附带的PoC证明仍然可以实现相当可靠的实际利用。

为了利用漏洞,我们需要在nf_tables_rule_destroy()下的当前CPU上释放匿名nft_set。然后,我们需要在另一个CPU上进行调度并以异步方式执行nf_tables_set_elem_destroy()以释放匿名nft_set中的元素。由于多个CPU同时在nft_set中进行争用,因此这个异步释放可以在使用nft_set之前而不是之后发生。

尽管nf_tables_set_elem_destroy()在使用nft_set之前异步释放nft_set,但是它也消耗了一些CPU时间。因此,有可能在同一个CPU上并行执行nf_tables_rule_destroy()和nf_tables_set_elem_destroy()。在这种情况下,nf_tables_set_elem_destroy()将在nf_tables_rule_destroy()释放匿名nft_set之前运行,从而使nf_tables_rule_destroy()中的nft_set指针指向的内存位置变成了未定义的。

解决方案

我们向Linux内核团队报告了这个问题。他们已经在内核分支中应用了我们的修补程序,该修补程序包括两个部分:

1.检查规则是否引用了任何已删除的表达式或集合,如果是,则不会将规则删除。

2.禁用nf_tables_set_elem_destroy()中的异步释放,以确保不会释放已释放的nft_set。

致谢

我们感谢Linux内核安全团队的快速响应和合作。在我们向他们报告漏洞的同时,他们已经在内核分支中应用了我们的修补程序。

参考链接

https://bugzilla.redhat.com/show_bug.cgi?id=2031748

https://lore.kernel.org/netdev/20220627174722.GA266089@kroah.com/

https://github.com/oferchen/POC-CVE-2023-32233

标签:工具分享, 漏洞分享, 本地提权