【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

admin 2021年12月15日04:06:12评论346 views字数 12290阅读40分58秒阅读模式

点击上方蓝字“Ots安全”一起玩耍

今年,国际竞赛 Pwn2Own Vancouver 再次于 4 月初举行。在不同的类别中,本地提权类别 (LPE) 建议使用两种主要操作系统:Linux (Ubuntu) 和 Windows 10。本文介绍了如何在本次比赛中发现和利用 Ubuntu 内核漏洞以获得 root 访问权限来自非特权用户。

介绍

在零日计划博客中宣布,2021 年版的 Pwn2Own Vancouver 鼓励在不同目标中搜索漏洞。我选择尝试使用 Ubuntu Local Escalation of Privilege 条目来寻找 Linux 内核漏洞。这篇博文将这次冒险从漏洞研究开始,到绕过内核保护(KASLR、SMAP)的利用。


此处介绍的漏洞在比赛期间被使用,并披露给 Ubuntu 团队,后者迅速对其进行了修补。CVE-2021-3492 已分配给此错误。由于补丁涉及Linux内核,更新后不要忘记重启。


受影响的版本

Shiftfs 驱动程序仅存在于 Ubuntu 内核中,因为它还没有上传到 kernel.org 内核中。


此漏洞已在桌面版和服务器版的 Ubuntu Groovy (20.10) 和 Ubuntu Focal (20.04) 上进行了检查。未检查以前版本的 Ubuntu。添加此漏洞的代码已于 2019 年 4 月提交。


要检查您的内核是否不易受到攻击,以下是通用内核 (x86) 的修补版本:

  • Groovy 版本已更正,因为内核版本:5.8.0-50.56

  • 核心版本已更正,因为内核版本:5.4.0-72.80


利用代码

Synacktiv Github 中提供了完整漏洞利用的源代码

参考 https://github.com/synacktiv/CVE-2021-3492/blob/master/exploit/main.c

此 PoC 是为 Ubuntu 20.10 (5.8.0-xx-generic) x86 64 位开发的。


来自非特权用户的内核攻击面

Pwn2Own的规则规定接受的漏洞必须是内核漏洞。尽管用户空间本身可以包含很多用于本地提权的表面,但我只针对 Ubuntu 发行版的 Linux 内核。可以下载用于构建当前内核包的内核源代码,并且有一个Git 存储库。


目标是从标准用户那里获得 root 权限 (UID 0)。在大多数 Linux 发行版上,root 用户和内核代码执行之间没有界限,因为 root 可以加载内核模块、在块设备上写入等。


在想知道非特权用户可以做什么之前,让我们列举一个进程如何与内核交互。用户产生的进程在 x86(ARM 上的 EL0)上执行环 3 中的指令。它只能访问自己的虚拟内存或需要执行系统调用才能与操作系统交互。列出内核攻击面的一个很好的起点是列出所有可用的系统调用。


系统调用由存储在寄存器中的数字标识。根据系统调用,用户态进程可以通过设置其他寄存器来提供参数。有多种方法可以列出可用的系统调用:

  • grep -R "SYSCALL.*_DEFINE" 在源代码中并检查它们是否被编译

  • __x64_sys_*在 vmlinux 或 /proc/kallsyms 中搜索符号

  • 查看编译后的二进制文件中的syscall表

  • 使用来自用户空间的内核跟踪器 (ftrace) 检查系统调用背后是否有内容

如果您尝试对每个可能的数字不带参数使用系统调用,您可能无法区分未实现的系统调用和正常错误之间的区别。所以我创建了一个小的 shell 脚本,它使用 ftrace 来定位每个可能的系统调用数量的内核处理程序。这是可用系统调用的结果列表。请注意,对于 64 位机器,32 位系统调用也可以完成,并由另一个使用 32 位参数的处理程序管理。

【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

系统调用列表只是与内核对话的第一个入口点。但不是使用新数字,而是许多系统调用导致多个函数。最常见的例子是有关的那些文件的描述,像open,read,write,ioctl,等。事实上文件描述符可以有很多的Linux系统上的不同的事情:

  • 由文件系统管理的 inode(文件或目录)。系统调用的内核处理程序取决于文件系统,因此每个支持/挂载的文件系统都有一个全新的表面。还有一些特殊的内核文件系统:procfs、sysfs、debugfs、cgroup 等。

  • 甲特殊由内核驱动程序(框或字符设备)允许与驱动程序通信提供的文件。通常,特殊文件位于 /dev 中。每个设备都提供了一个有趣的攻击面。

  • 来自socket系统调用的用于网络通信的句柄。底层内核处理程序取决于所使用的网络协议类型,并且有很多扭曲的用法。

  • 其他几个基于文件描述符的 API:共享内存、管道、pidfd、io_uring、...


所以从这少量的系统调用中,有一个巨大的内核表面。然而,许多内核可访问的代码只能从已经有特权的用户那里获得。为了限制非特权用户的功能,内核通常使用功能(请参阅 参考资料man 7 capabilities)。限制功能的另一种方法是为来自 VFS(来自文件路径)的所有表面设置文件权限。例如,Ubuntu 只允许root读/写块设备 /dev/mem(可能会被删除)。


此外,逐渐添加了许多极端情况和特殊行为以支持新功能,同时保持与以前的用户空间代码的兼容性。其中一些新功能已添加到容器中。例如,命名空间用于在内核对象(进程、VFS、IPC 等)上添加更多隔离。容器有两种方法:


容器是从根进程启动的,并且除了用户命名空间之外,还具有独立的命名空间。这意味着容器内的 UID 与主机上的相同(从内核角度来看)。在这种情况下,有几个新的限制可以防止容器内的根进程影响主机。例如,删除了许多功能。


容器是从使用新用户命名空间的特权用户启动的。创建此新命名空间时,会映射 UID。所以一个进程在它自己的用户命名空间中可以是 UID 0。它还可以获得所有功能,但它们仅在此用户命名空间内有意义。为了支持这种行为,对于特权操作,内核不检查进程是否具有能力,而是检查它是否在根命名空间内具有能力。


这听起来很奇怪,因为这两种方法完全不同。许多发行版禁止从非特权用户创建新的用户命名空间。由于该功能是 Linux 内核中的主线,因此可以通过 systctl 选项(系统范围内的)禁用/启用此功能:

$ sudo sysctl kernel.unprivileged_userns_clonekernel.unprivileged_userns_clone = 1

非特权用户可以在 Ubuntu 发行版上访问更多内核代码,因为默认情况下启用非特权用户命名空间。使用unshare或clone系统调用,可以创建一个新的用户命名空间,然后获得所有功能。当然,这些功能仅适用于用户拥有的资源,但现在允许了一些通常禁止普通用户执行的操作。在这些操作中,用户可以创建其他类型的命名空间(挂载、网络等)并在其挂载命名空间内挂载多个文件系统。

$ unshare -U -r # create a userns where uid 0 is mapped on current user$ unshare -m    # create a mount namespace$ mount -t tmpfs none [dir]

并非所有文件系统都可以作为普通用户挂载。在内核代码中,该标志FS_USERNS_MOUNT用于定义给定的文件系统是否可以由非 root 用户挂载。这个标志是为一些文件系统设置的:

  • android/binderfs

  • mqueue

  • shmem

  • sysfs

  • ramfs (tmpfs)

  • overlayfs

  • proc

  • aufs

  • fuse

  • shiftfs

  • devpts

  • cgroup

此外,即使文件系统驱动程序是内核模块 (.ko),它也会在发出挂载系统调用时自动加载。


在漏洞研究中,如果之前有很多审计,有趣的目标是:

  • 不太常见的表面。在这里,带有用户名称空间的表面可能是一个不错的选择,因为许多其他发行版都阻止了它。

  • 最近添加的或特定于目标的代码。例如,来自 Ubuntu 的非主线修改(因此不存在于其他发行版中)。

因此,我审查的第一个驱动程序是shiftfs,因为该驱动程序仅存在于 Ubuntu 内核中,鲜为人知且可由非特权用户安装。幸运的是,我在这个驱动程序中发现了一个足够强大的漏洞,可以用这个单一的错误执行本地权限提升。


ShiftFS 双免费漏洞 (CVE-2021-3492)

此处介绍的漏洞存在于文件系统驱动程序shiftfs 中。这是一种覆盖类型的文件系统,允许绑定挂载目录并使用用户名称空间所有者(已执行挂载的人)的映射来移动 UID 和 GID。这对于有效引导非特权容器似乎很有用。我发现只有两个资源描述了该功能:

  • https://discuss.linuxcontainers.org/t/trying-out-shiftfs/5155

  • https://lwn.net/Articles/687354/

要访问文件系统代码,我们需要调用特定于该文件系统的“mount”函数。如前所述,可以创建一个用户命名空间并挂载它:

$ mkdir d1 d2$ unshare -U -r$ unshare -m$ mount -t tmpfs none d1$ mount -t shiftfs -o mark,passthrough=2 d1 d2

目录内的所有文件操作d2将首先由 shiftfs 驱动程序处理。


当使用选项“mark”和“passthrough=2”挂载时,shiftfs 文件系统会处理特殊的 ioctl,主要是将它们转发到下面的文件系统。

【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

如果 ioctl 编号在白名单中,shiftfs_real_ioctl则调用该函数。

static long shiftfs_real_ioctl(struct file *file, unsigned int cmd, unsigned long arg){int newfd = -EBADF;long err = 0, ret = 0;void __user *argp = (void __user *)arg;struct btrfs_ioctl_vol_args *btrfs_v1 = NULL;struct btrfs_ioctl_vol_args_v2 *btrfs_v2 = NULL;
ret = shiftfs_btrfs_ioctl_fd_replace(cmd, argp, &btrfs_v1, &btrfs_v2, &newfd);if (ret < 0)return ret;
// here wrapper to vfs_ioctl()// ...
err = shiftfs_btrfs_ioctl_fd_restore(cmd, newfd, argp, btrfs_v1, btrfs_v2);if (!ret) ret = err;
return ret;}

当使用BTRFS_IOC_SNAP_CREATEioctl 时,该函数shiftfs_btrfs_ioctl_fd_replace用于将结构从用户空间复制到内核空间。此代码的目标是通过将结构 btrfs_ioctl_vol_args 中包含的文件描述符替换为链接到底部文件系统中的 inode 的新文件描述符来包装遗留的 btrfs ioctl。该结构的长度为 4096 字节,如用户 API 标头中所定义:

#define BTRFS_PATH_NAME_MAX 4087struct btrfs_ioctl_vol_args {    __s64 fd;char name[BTRFS_PATH_NAME_MAX + 1];};

如果在 shiftfs 管理的文件上执行此 ioctl,则处理代码会将结构 btrfs_ioctl_vol_args 从用户空间复制到内核空间。替换此结构中的 fd 字段后,将其复制回用户空间:

static int shiftfs_btrfs_ioctl_fd_replace(int cmd, void __user *arg,struct btrfs_ioctl_vol_args **b1,struct btrfs_ioctl_vol_args_v2 **b2,int *newfd){// ...
if (cmd == BTRFS_IOC_SNAP_CREATE) { v1->fd = *newfd; ret = copy_to_user(arg, v1, sizeof(*v1)); v1->fd = oldfd; } else {// ... }
if (ret) shiftfs_btrfs_ioctl_fd_restore(cmd, *newfd, arg, v1, v2);
return ret;
}

该函数copy_to_user返回剩余字节数。如果在复制过程中发生故障,则返回正值。在调用函数中shiftfs_real_ioctl,通过检查返回值是否为负来检测错误。因此,即使发生错误,也会执行标称路径,导致shiftfs_btrfs_ioctl_fd_restore第二次调用。这个函数执行另一个copy_to_user将结构再次发送btrfs_ioctl_vol_args到用户空间然后释放它。

static int shiftfs_btrfs_ioctl_fd_restore(int cmd, int fd, void __user *arg,struct btrfs_ioctl_vol_args *v1,struct btrfs_ioctl_vol_args_v2 *v2){int ret;
if (!is_btrfs_snap_ioctl(cmd))return 0;
if (cmd == BTRFS_IOC_SNAP_CREATE) ret = copy_to_user(arg, v1, sizeof(*v1));else ret = copy_to_user(arg, v2, sizeof(*v2));
__close_fd(current->files, fd); kfree(v1); kfree(v2);
return ret;}

只能同时设置v1或之一v2。两种结构都会导致相同的漏洞并具有相同的大小。调用两次shiftfs_btrfs_ioctl_fd_restore导致:

  1. 关闭两次相同的 fd

  2. 释放两次相同的结构

shiftfs_btrfs_ioctl_fd_replace在从用户空间复制期间已分配此结构:

v1 = memdup_user(arg, sizeof(*v1));

核函数同时memdup_user执行copy_from_user分配和分配。此分配使用带有 GFP_USER 标志的kmalloc,它使用与 GFP_KERNEL 相同的平板。


总而言之,当出现故障时copy_to_user:

【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

虽然可以在此分配上触发双重释放,但也有可能忘记分配,从而永远失去此内存。实际上,如果 的值v1->fd针对当前进程中的无效文件描述符,则该函数返回负错误并且kfree永远不会执行。这个错误允许任何用户填满整个内核内存,直到系统内存不足。


同步用户空间和内核空间

双重释放允许通过在两次释放操作之间分配相同类型的内核内存块并在第二次释放后喷射受控对象来接管相同类型的内核内存块。所以需要以下元素:

  • 一种在第一个空闲和第二个空闲之间阻塞内核的方法。

  • 受害者:相同类型的内存块 (kmalloc-4096),它可以提供漏洞利用原语,如内核内存读/写。


一种喷射技术,允许创建相同类型的内存块并用受控数据填充其内容。

Ubuntu 内核配置已经CONFIG_PREEMPT_VOLUNTARY设置。这意味着内核在执行内核代码时不会被调度程序抢占,除非代码自愿要求它。然而,有几种已知的技术允许在执行某些操作时阻塞内核。在我们的例子中,在第一个 free 和第二个 free 之间调用的几个函数可以被阻塞:

  • vfs_ioctl在较低的 inode 上执行。使用带有 FUSE 的用户控制的文件系统可以阻止对文件的任何操作。

  • copy_to_user在每次调用 kfree 之前执行。使用 userfaultfd 允许阻止操作。

  • close_fd在每次调用 kfree 之前执行。此操作可以使内核在特殊条件下休眠。


该userfaultfd方法已在利用被使用。Ubuntu groovy (20.10) 的内核引入了一项新功能,允许用户空间程序根据写入错误管理其页面。虽然可以根据页面错误管理页面,但现在可以定义写保护页面并在执行写入时由内核调用。


它的用法很简单,漏洞利用创建了两个线程:一个在copy_to_user(内核模式)期间触发写入错误,而另一个处理此错误。在此处理期间(不受时间限制),第一个线程在 shiftfs 内核代码中并被阻塞,直到第二个线程选择解除阻塞的情况。

【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

该漏洞利用分配了两页用户空间内存。使用在这两个页面中使用字节的结构调用易受攻击的 ioctl。通过将第一页设置为写保护,在copy_to_user. 在故障处理中,第二页被标记为写保护,而第一页被设置为可写。


此方法允许copy_to_user在同一结构上阻止内核执行的多个操作。copy_to_user在第一次释放之后进行的事实意味着,如果在受害者被分配(对于第一页)后 userfaultfd 回调解除内核阻塞,则受害者内容被复制到用户空间内存。


下图显示了漏洞利用所使用的内存映射和保护序列:

【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

在同一个 SLAB 中寻找受害者

Linux 内核有几种分配内存的方法。大多数分配使用kmalloc由 SLUB 分配器管理。当驱动程序需要一块内存时,会根据请求的大小使用相应的缓存。例如,这个 bug 中使用的内存大小为 4096 :SLUB 分配器将使用缓存kmalloc-4096。由于存在用于上的两个电源对齐尺寸缓存,这个高速缓存包含用于分配的大小的块2097 4096之间的高速缓存中含有的列表板坯的存储器几个连续块组成。当kfree被调用时,块被添加到free_list它的slab中。


要使用双重空闲来接管块,我们只需要确保以下几点:

  • 第一个法线kfree是在已知的 CPU 上进行的

  • 受害者被分配在相同的 CPU 上,使用kmalloc兼容的大小来使用相同的平板。因为每个 CPU 都有一个始终对应最新的slabkfree,所以返回的块与 1 中的相同。

  • 第二辆马车kfree制作完成。

  • 在同一 CPU 上进行另一次分配,大小相同,以控制受害者的新内容。

该漏洞利用在同一 CPU 上使用 执行所有这些操作sched_setaffinity,这对于非特权用户是允许的。为了防止其他内核分配的竞争,它们必须在一个小的时间窗口内进行。


但是如何找到合适的受害者呢?我们需要让内核在 2097 到 4096 字节之间分配一个块,这对于了解 KASLR 偏移和/或获得某种强大的原语(如任意写入或调节执行流程的方法)很有用。


我使用了两种方法来定位受害者:


  • 我构建了一个合适对象的数据库。使用相同的源代码和相同的配置,构建了一个带有调试数据的vmlinux。然后,使用pahole,提取了所有内核结构名称和大小。由于内核通常分配结构大小的块,因此这是寻找受害者的好方法。

  • 在运行时,可以使用内核跟踪器来显示内核所做的所有分配。通过过滤分配大小,可以通过生成系统活动(例如,启动浏览器……)来找到潜在的受害者。

事实证明,潜在的受害者并不多。希望我找到了一个有趣的,它也可以从非特权用户触发:devinet_sysctl_table。

static struct devinet_sysctl_table {struct ctl_table_header *sysctl_header;struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];} devinet_sysctl= {    .devinet_vars = {        DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",devinet_sysctl_forward),        DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),        DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),// ...    }}

该漏洞利用的受害者是网络 sysctl 表之一。这些表是在启动时为主网络堆栈创建的。但是,网络命名空间使用专用的 sysctl 表。可以使用路径 /proc/sys/net/ 中的 procfs 文件系统读取和写入这些配置值。


创建新的网络命名空间时,分配的第一个 kmalloc-4096 块存储堆栈的 IPv4 通用 sysctl 表。该表由文件 net/ipv4/devinet.c 管理。分配的是全局表 devinet_sysctl_table 的副本。这可以在执行以下操作后使用内核跟踪器进行检查unshare -n:

bash-1912    [002] ....  5515.306551: kmalloc: call_site=__devinet_sysctl_register+0x47/0x110 ptr=00000000552c19f5 bytes_req=2120 bytes_alloc=4096 gfp_flags=GFP_KERNELbash-1912    [002] ....  5515.306595: kmalloc: call_site=__devinet_sysctl_register+0x47/0x110 ptr=000000007dd47f0d bytes_req=2120 bytes_alloc=4096 gfp_flags=GFP_KERNELbash-1912    [002] ....  5515.306742: kmalloc: call_site=mr_table_alloc+0x42/0x100 ptr=00000000598ab799 bytes_req=3608 bytes_alloc=4096 gfp_flags=GFP_KERNEL|__GFP_ZERObash-1912    [002] ....  5515.306766: kmalloc: call_site=ipv4_mib_init_net+0xf4/0x1a0 ptr=00000000d0d71277 bytes_req=4096 bytes_alloc=4096 gfp_flags=GFP_KERNEL|__GFP_ZERObash-1912    [002] ....  5515.306811: kmalloc: call_site=__register_sysctl_table+0x50/0x1e0 ptr=000000001ae330cc bytes_req=3216 bytes_alloc=4096 gfp_flags=GFP_KERNEL|__GFP_ZERObash-1912    [002] ....  5515.306922: kmalloc: call_site=ipv6_init_mibs+0xb2/0x110 ptr=00000000dd7d83c1 bytes_req=4096 bytes_alloc=4096 gfp_flags=GFP_KERNEL|__GFP_ZERO

创建网络命名空间会分配匹配相同块大小的其他块。当进行多次分配时,CPU Slab 可能会被新的 Slab 替换,从而丢失插入全局部分列表中的牺牲 Slab(包含未绑定到 CPU 的空闲块的 Slab)。为了取回这个slab,漏洞利用分配了比一个slab中的块数更多的块。在我们的例子中,一个slab包含8个块,为了取回受害者slab,最后分配了8个以上的块。


所选择的受害者非常有趣,因为它包含一个ctl_table定义如下的结构:

struct ctl_table {const char  *              procname;             /*     0   0x8 */void *                     data;                 /*   0x8   0x8 */int                        maxlen;               /*  0x10   0x4 */umode_t                    mode;                 /*  0x14   0x2 */struct ctl_table *         child;                /*  0x18   0x8 */        proc_handler *             proc_handler;         /*  0x20   0x8 */struct ctl_table_poll *    poll;                 /*  0x28   0x8 */void *                     extra1;               /*  0x30   0x8 */void *                     extra2;               /*  0x38   0x8 */};

该proc_handler字段是一个函数指针!通过泄漏其原始值,可以检索 KASLR 偏移量。对于网络 sysctl,第一个指针devinet_sysctl_forward用于通过从内核符号文件中减去其基地址来计算 KASLR 偏移量。


此外,sysctl 头地址在结构的开头泄漏。这是一个独特的分配和转储其内容允许检索受害者的地址。


使用的喷洒技术很简单:它使用了漏洞 (BTRFS_IOC_SNAP_CREATE) 的数倍相同的 ioctl。实际上,系统调用执行了 4096 字节的分配。通过在fd字段中设置无效的文件描述符,永远不会释放分配,这对于修复双重释放非常有用。对喷射内容的唯一限制是在第一个字节上,因为该字段fd由 shiftfs 代码修补。安装 R/W 原语后,受害者的初始内容的第一个字节将被恢复。


使用 sysctl 表稳定原语

sysctl 表中的条目由结构定义,ctl_table并被块重用和喷射技术覆盖。


内核读/写可以通过将指针数据设置到给定的目的地并使用该函数proc_doulongvec_minmax作为proc_handler. 当用户空间进程读取和写入 /proc/sys/net/ipv4/conf/all 中的文件时,此函数将在数据指向的内核内存中读取和写入任意数量的 64 位值。


该漏洞利用接管了几个 sysctl:

  • sysctl0 : /proc/sys/net/ipv4/conf/all/forwarding: 用于转储 sysctl_header 并获取受害者的地址

  • sysctl1 : /proc/sys/net/ipv4/conf/all/mc_forwarding: 此 sysctl 设置为覆盖主命名空间 (sys.debug.exception-trace) 中的全局 sysctl 以获得可重用的 R/W 原语。在受害者地址被泄露并写入 sysctl2 的内容后,全局 sysctl 表被恢复。

  • sysctl2 : /proc/sys/net/ipv4/conf/all/bc_forwarding: 目标 sysctl3 并允许更改读/写的目的地(数据字段)。

  • sysctl3 : /proc/sys/net/ipv4/conf/all/accept_redirects: 用于 R/W 内核内存,此表由 sysctl2 修补。

  • sysctl4 : /proc/sys/net/ipv4/conf/all/secure_redirects: 用于调用函数,该表由 R/W 原语修补。

【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

原语稳定后,漏洞利用程序可以通过读写/proc中的文件来读写内核内存。


内核代码执行和root权限

请注意,稳定的内核读/写足以获得用户级 root 访问权限。但是由于我们已经有了一个调用原语,我决定通过内核代码执行来实现。防止从内核执行用户态代码的主要内核保护是 SMAP。该保护可防止内核本身访问或执行专用功能(如copy_{from|to}_user.


不应同时存在具有写入和执行权限的映射。然而,遗留内核代码调用set_memory_x将受害者页面重新映射为可执行文件的函数。


要调用此函数,将 sysctl4 与修补的proc_handler. 当它被调用时,第一个参数是ctl_table. 将数据写入 sysctl 文件(在 /proc 中)时,第二个参数设置为 1。因此,在调用 时set_memory_x,第二个参数将为 1,即要重新映射为可执行文件的页数。由于它有 4096 字节长,受害者仅由一页组成,但表的地址在页开始时没有对齐。set_memory_x与未对齐的地址一起使用时,其页面仍会重新映射,但WARN_ON宏会在 dmesg 中显示警告。


使用这种技术,受害者内存被设置为可执行。一个 shellcode 被写入这个内存中,然后 sysctl4 被修补以调用这个有效载荷。shellcode 执行通常的代码来获取当前进程的 root 凭据:

commit_cred(prepare_kernel_cred(0));

最后,漏洞利用会产生一个新的 shell。


结论

在搜索本地权限提升漏洞时,我发现了很多我不知道的功能,并学习了几种利用技术(userfaultfd、spray 技术、块重用等)。我很幸运在我审核的第一批司机中找到了一些东西。


double free 漏洞非常危险,因为它们通常仅使用一个 bug 即可被利用。正如所呈现的错误所见,它在提供块重用的同时提供信息泄漏。即使没有这个信息泄露,劫持一个已知的内核对象也可能就足够了。


这些sysctl表是非常有趣的利用目标。对全局结构(如果 KALSR 偏移被解析,则地址是已知的)的单次写入允许获得可重用的 R/W 原语。使用这些原语,可以通过各种方式获得根权限。


如果您不使用非特权容器,我建议设置kernel.unprivileged_userns_clone为 0 以减少攻击面。


这是我第一次参加 Pwn2Own。我要感谢 ZDI 组织 Pwn2Own 和 Ubuntu 内核团队的参与和他们修复错误的反应。


资源

Ubuntu 修复

https://ubuntu.com/security/CVE-2021-3492



其他关于内核开发的博文:

CVE-2017-11176:

  • https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part1.html

  • https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part2.html

  • https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html

  • 关于 Userfaultfd:https ://blog.lizzie.io/using-userfaultfd.html

  • 关于 Userfaultfd 和 Sprays:https ://duasynt.com/blog/linux-kernel-heap-spray

CVE-2017-7308:

  • https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html

  • https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308

【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)

本文始发于微信公众号(Ots安全):【漏洞利用】利用 UBUNTU SHIFTFS 驱动程序中的 DOUBLE FREE 漏洞 (CVE-2021-3492)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月15日04:06:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【漏洞利用】利用UBUNTUSHIFTFS驱动程序中的DOUBLEFREE漏洞(CVE-2021-3492)http://cn-sec.com/archives/445863.html

发表评论

匿名网友 填写信息