Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
StackRot — CVE-2023-3269: Linux内核权限提升漏洞 | Kitploit
工具/GitHubGitHub/lrh2000/stackrot
权限提升漏洞分析漏洞利用CTF二进制利用
GitHublrh2000/stackrot

StackRot

CVE-2023-3269: Linux内核权限提升漏洞

查看仓库
4993797个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

StackRot (CVE-2023-3269): Linux 内核权限提升漏洞

GitHub CI (GitHub-CI-verified exploit)

Demo

在 Linux 内核 6.1 至 6.4 版本的堆栈扩展处理中发现了一个漏洞,俗称“Stack Rot”。负责管理虚拟内存区域的 maple 树在节点替换时未能正确获取 MM 写锁,从而导致释放后使用(use-after-free)问题。低权限的本地用户可利用此漏洞危害内核并提升权限。

由于 StackRot 是内存管理子系统中的 Linux 内核漏洞,它几乎影响所有内核配置,且触发所需权限极低。但需要注意的是,maple 节点通过 RCU 回调函数释放,实际内存释放会延迟到 RCU 宽限期之后才能进行。因此,利用该漏洞具有一定挑战性。

据我所知,目前尚无公开可用的针对 RCU 释放后使用(UAFBR)漏洞的利用程序。这是首次证明 UAFBR 漏洞可被利用,即使在没有 CONFIG_PREEMPT 或 CONFIG_SLAB_MERGE_DEFAULT 配置的情况下。值得注意的是,该利用已在 Google kCTF VRP 提供的环境(bzImage_upstream_6.1.25,config)中成功演示。

StackRot 漏洞自 Linux 内核版本 6.1 起便存在,该版本中 VMA 树结构从红黑树改为了 maple 树。

背景

每当使用 mmap() 系统调用来建立内存映射时,内核会创建一个名为 vm_area_struct 的结构体来表示对应的虚拟内存区域(VMA)。该结构体存储了包括标志、属性以及与映射相关的其他必要信息在内的各种数据。```c struct vm_area_struct { long unsigned int vm_start; /* 0 8 / long unsigned int vm_end; / 8 8 / struct mm_struct * vm_mm; / 16 8 / pgprot_t vm_page_prot; / 24 8 / long unsigned int vm_flags; / 32 8 / union { struct { struct rb_node rb attribute((aligned(8))); / 40 24 / / --- cacheline 1 boundary (64 bytes) --- / long unsigned int rb_subtree_last; / 64 8 / } attribute((aligned(8))) shared attribute((aligned(8))); / 40 32 / struct anon_vma_name * anon_name; / 40 8 / } attribute((aligned(8))); / 40 32 / / --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- / struct list_head anon_vma_chain; / 72 16 / struct anon_vma * anon_vma; / 88 8 / const struct vm_operations_struct * vm_ops; / 96 8 / long unsigned int vm_pgoff; / 104 8 / struct file * vm_file; / 112 8 / void * vm_private_data; / 120 8 / / --- cacheline 2 boundary (128 bytes) --- / atomic_long_t swap_readahead_info; / 128 8 / struct vm_userfaultfd_ctx vm_userfaultfd_ctx; / 136 0 */

    /* size: 136, cachelines: 3, members: 14 */
    /* forced alignments: 1 */
    /* last cacheline: 8 bytes */

} attribute((aligned(8)));

随后,当内核遇到缺页异常或其他与内存相关的系统调用时,需要仅根据地址快速查找VMA。以前,VMA 是通过红黑树管理的。但从 Linux 内核 6.1 版本开始,逐渐迁移到了枫树(maple trees)。[枫树][mt] 是一种 RCU 安全的 B 树数据结构,针对存储非重叠区间进行了优化。尽管如此,其复杂的结构增加了代码库的复杂性,并引入了 StackRot 漏洞。

 [mt]: https://docs.kernel.org/6.4/core-api/maple_tree.html

从本质上看,枫树由枫树节点组成。虽然树的结构可能很复杂,但需要注意的是,这种复杂性与 StackRot 漏洞无关。因此,在本文中,我们假设枫树只包含一个节点,即根节点。

这个根节点最多可以包含 16 个区间。这些区间要么表示一段间隙,要么指向一个 VMA。由于间隙也算作区间,所有区间是连续连接的,因此节点结构中只需要 15 个端点(也称为枢轴)。请注意,最左端点和最右端点被省略了,因为它们可以从父节点中获取。```c
struct maple_range_64 {
        struct maple_pnode *       parent;               /*     0     8 */
        long unsigned int          pivot[15];            /*     8   120 */
        /* --- cacheline 2 boundary (128 bytes) --- */
        union {
                void *             slot[16];             /*   128   128 */
                struct {
                        void *     pad[15];              /*   128   120 */
                        /* --- cacheline 3 boundary (192 bytes) was 56 bytes ago --- */
                        struct maple_metadata meta;      /*   248     2 */
                };                                       /*   128   128 */
        };                                               /*   128   128 */

        /* size: 256, cachelines: 4, members: 3 */
};

如上所示,maple_range_64 结构表示一个 maple 节点。除了枢轴点之外,当节点作为叶节点时,槽位用于引用 VMA 结构;当节点作为内部节点时,槽位用于引用其他 maple 节点。如果某个间隔对应一个缺口,则槽位将简单地包含一个 NULL 值。枢轴点和槽位的排列可如下图所示:``` Slots -> | 0 | 1 | 2 | ... | 12 | 13 | 14 | 15 | ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ └─ Implied maximum │ │ │ │ │ │ │ └─ Pivot 14 │ │ │ │ │ │ └─ Pivot 13 │ │ │ │ │ └─ Pivot 12 │ │ │ │ └─ Pivot 11 │ │ │ └─ Pivot 2 │ │ └─ Pivot 1 │ └─ Pivot 0 └─ Implied minimum

关于并发修改,maple树施加了一项特定限制,即写入者必须持有排他锁(*规则W*)。对于VMA树而言,此排他锁对应MM写锁。至于读取者,有两种选择。第一种选择是持有MM读锁(*规则A1*),这将导致写入者被MM读写锁阻塞。另一种选择是进入RCU临界区(*规则A2*)。通过这样做,写入者不会被阻塞,且读取者可以继续其操作,因为maple树是RCU安全的。虽然大多数现有的VMA访问采用第一种选择(即规则A1),但规则A2在一些性能关键场景中使用,例如无锁页错误。

然而,还有一个需要特别关注的方面,涉及栈扩展。栈代表一个使用MAP_GROWSDOWN标志映射的内存区域,表明当访问该区域下方的地址时会自动扩展。在这种情况下,相应VMA的起始地址以及maple树中关联的区间会被调整。值得注意的是,这些调整是在未持有MM写锁的情况下进行的。```c
static inline
void do_user_addr_fault(struct pt_regs *regs,
                        unsigned long error_code,
                        unsigned long address)
{
	// ...

	if (unlikely(!mmap_read_trylock(mm))) {
		// ...
	}
	// ...
	if (unlikely(expand_stack(vma, address))) {
		// ...
	}

	// ...
}

通常,栈VMA与其相邻VMA之间存在间隙,因为内核强制实施栈保护。在这种情况下,当扩展栈时,只需要更新maple节点中的pivot值,此过程可以原子性地执行。然而,如果相邻VMA也拥有MAP_GROWSDOWN标志,则不会强制实施栈保护。```c int expand_downwards(struct vm_area_struct *vma, unsigned long address) { // ...

if (prev) {
	if (!(prev->vm_flags & VM_GROWSDOWN) &&
	    vma_is_accessible(prev) &&
	    (address - prev->vm_end < stack_guard_gap))
		return -ENOMEM;
}

// ...

}

因此,栈扩展可以消除间隙。在这种情况下,必须移除枫树节点内的间隙区间。由于枫树是RCU安全的,无法在原地覆盖节点。相反,会创建一个新节点,触发节点替换,旧节点随后通过RCU回调销毁。```c
static inline void mas_wr_modify(struct ma_wr_state *wr_mas)
{
	// ...

	if ((wr_mas->offset_end - mas->offset <= 1) &&
	    mas_wr_slot_store(wr_mas))           // <-- in-place update
		return;
	else if (mas_wr_node_store(wr_mas))      // <-- node replacement
		return;

	// ...
}

只有在所有预先存在的 RCU 临界区结束后,RCU 回调函数才会被调用。然而,访问 VMAs 时会出现问题,因为仅持有 MM 读锁,并且它并未进入 RCU 临界区(根据规则 A1)。因此,在理论上,回调函数可能随时被触发,导致旧的 maple 节点被释放。但指向旧节点的指针可能已被获取,从而在后续尝试访问时引发释放后使用错误。

发生释放后使用(UAF)的回溯如下所示:```

  • CPU 0 - - CPU 1 -

mm_read_lock() mm_read_lock() expand_stack() find_vma_prev() expand_downwards() mas_walk() mas_store_prealloc() mas_state_walk() mas_wr_story_entry() mas_start() mas_wr_modify() mas_root() mas_wr_node_store() node = rcu_dereference_check() mas_replace() [ The node pointer is recorded ] mas_free() ma_free_rcu() call_rcu(&mt_free_rcu) [ The node is dead ] mm_read_unlock()

下载工具