Linux Kernel ROP

来源:互联网 发布:无法连接steam网络 编辑:程序博客网 时间:2024/06/06 20:19

Kernel ROP

内核ROP(返回导向编程)是一种有用的技术,通常用于绕过与非可执行内存区域相关的限制。例如,在默认内核1上,它提供了绕过内核和用户地址分隔缓解的实用方法,例如最近的Intel CPU上的SMEP(Supervisor Mode Execution Protection)。

本教程的目标是演示如何构建内核ROP链以提升用户权限。作为结果,需要满足以下要求:

1 执行特权升级有效载荷2 可以引用驻留在用户空间中的数据(即,允许从用户空间获取数据)3 驻留在用户空间中的指令可能无法执行

在典型的ret2usr攻击中,内核执行流程被重定向到包含特权升级有效载荷的用户空间地址:

void __attribute __((regparm(3)))payload(){        commit_creds(prepare_kernel_cred(0);}

上述特权升级有效负载分配一个新的凭据结构(使用uid = 0,gid = 0等)并将其应用于调用进程。我们可以构建一个将执行上述操作的ROP链,而不执行驻留在用户空间中的任何指令,即不将程序计数器设置为任何用户空间的内存地址。最终目标是使用ROP链执行内核空间中的整个特权升级有效负载。然而,这在实践中可能不是必需的。例如,为了绕过SMEP,使用ROP链来翻转SMEP位就可以了,然后可以在用户空间中执行标准权限升级有效负载。

基于上述有效载荷的ROP链应类似于以下内容:

这里写图片描述

使用x86_64调用约定,函数的第一个参数在%rdi寄存器中传递。因此,ROP链中的第一条指令从堆栈中弹出空值。然后将该值作为第一个参数传递给prepare_kernel_cred()。cred将存储指向新结构体的指针,%rax然后可以将其指向另一个%rdi参数,并作为第一个参数传递commit_creds()。现在,我们有意地跳过了一些应用凭证后返回用户空间的细节。稍后将在本教程第2部分的“修复”部分中讨论这些细节。

在这部分中,我们将讨论如何找到有用的Gadgets,并构建特权升级ROP链。然后,我们将描述稍后使用的易受攻击的驱动程序代码(在本教程的第2部分中)来演示ROP链。

测试系统

对于本教程的其余部分,我们将使用Ubuntu 12.04.5 LTS(x64)与以下存储内核:

这里写图片描述

如果您想跟随并使用相同的内核,则ROPGadgets的所有地址应与我们的相同。

Gadgets

与用户空间应用程序类似,可以从内核二进制文件中简单地提取ROPGadgets。但是,我们需要考虑以下几点:

1 我们需要ELF(vmlinux)映像从中提取Gadgets。如果我们正在使用/boot/vmlinuz*图像,则需要首先解压缩2 一种专门用于提取ROPGadgets的工具是首选。

/boot/vmlinuz*是一个压缩的内核映像(使用各种压缩算法)。它可以使用extract-vmlinux位于内核树中的脚本来提取。

这里写图片描述

ROP技术利用代码未对准来识别新的Gadgets。这可能是由于x86语言密度,即x86指令集足够大(并且指令具有不同的长度),几乎任何字节序列都可以解释为有效指令。例如,根据偏移量,可以不同地解释以下指令(请注意,第二条指令表示有用的堆栈枢轴):

这里写图片描述

简单地objdump针对未压缩的内核映像运行,然后对Gadgets进行灰名单,只会产生所有可用Gadgets的一小部分(因为我们仅使用对齐的地址)。值得一提的是,在大多数情况下,这足以找到所需的Gadgets。

更有效的方法是使用专门设计用于识别ELF二进制文件中的Gadgets的工具。例如,ROPgadget可用于标识所有可用的Gadgets:

这里写图片描述

请注意,使用英特尔语法的ROPgadget工具。现在我们可以搜索我们的特权分级ROP链中列出的ROPGadgets。我们需要的第一个Gadgets是pop %rdi; ret:

这里写图片描述

显然可以使用上面的任何Gadgets。但是,如果我们决定使用其中一个Gadgetsret [some_num],那么我们将需要相应地构建我们的ROP链,同时考虑到堆栈指针将会增加(记住堆栈向下更低的内存地址增长)的事实[some_num]。我们将在本教程的第2部分中实践证明这一点。请注意,Gadgets可能位于不可执行的页面中。在这种情况下,必须找到替代的Gadgets。

mov %rax, %rdi; ret测试内核中没有Gadgets。但是,有几个Gadgetsmov %rax, %rdi后面跟着一个call指令:

这里写图片描述

我们可以调整我们最初的ROP链,以适应call通过加载的地址的指令commit_creds()进入%rbx。在call随后的指令将执行commit_creds()与%rdi指向我们的新的“根”名气结构。

这里写图片描述

执行上述ROP链应将当前进程的权限升级为root。

易受攻击的驱动

为了简化开发过程并在实践中展示内核ROP链,我们开发了以下易受攻击的驱动程序:

这里写图片描述

在[1]中,没有对数组的绑定检查。用户提供的偏移量足够大(以unsigned long表示)来访问用户或内核空间中的任何内存地址。

该驱动程序注册该/dev/vulndrv设备并ops在加载时打印阵列地址:

这里写图片描述

我们可以通过从用户空间提供的ioctl到达易受攻击的路径:

这里写图片描述

触发源代码如下所示:

这里写图片描述

通过提供预先计算的偏移量,可以执行内核空间中的任何内存地址。我们可以明确指出fn()我们的mmap’d用户空间内存地址(包含特权升级有效载荷),但请记住初始要求:不应执行驻留在用户空间中的指令。

我们需要一种方法来将内核执行流重定向到用户空间中的ROP链,而不执行任何用户空间指令。

我们故意跳过了一些有关枢转到我们的ROP链的细节,一旦特权被提升就固定系统。我们将在第2部分中讨论这些主题。在第2部分可用之前,鼓励您自行尝试此练习:) GitHub上提供了内核驱动程序和用户空间触发程序的源代码。

原创粉丝点击