首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在内核空间执行用户空间中的攻击代码?

如何在内核空间执行用户空间中的攻击代码?
EN

Security用户
提问于 2015-08-04 03:24:27
回答 1查看 1.1K关注 0票数 1

我正在阅读有关Ubuntu-12.10安全漏洞CVE-2013-1763的文章,该漏洞描述了堆栈缓冲区溢出的漏洞,该漏洞可被利用来提升进程的权限并获得root访问。

硬编码的攻击是在链接:https://github.com/spinlockirqsave/examples/blob/master/hacker/ubuntu_信贷/主要c上进行的。

关于同样的问题,我有几个问题:

  1. sdiag_family=0x37是怎么决定的?
  2. 确切的控制流是什么,一旦发送了套接字请求,权限提升代码x()是如何在用户空间中执行的?
  3. sdiag_family=0x37mmap_start=0x1a000值之间有什么关系吗?
EN

回答 1

Security用户

发布于 2015-08-04 13:21:29

如果您查看受影响代码 (来自内核3.7.0,第115至132行):

代码语言:javascript
复制
static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
        int err;
        struct sock_diag_req *req = nlmsg_data(nlh);
        const struct sock_diag_handler *hndl;

        if (nlmsg_len(nlh) < sizeof(*req))
                return -EINVAL;

        hndl = sock_diag_lock_handler(req->sdiag_family);
        if (hndl == NULL)
                err = -ENOENT;
        else
                err = hndl->dump(skb, nlh);
        sock_diag_unlock_handler(hndl);

        return err;
}

sock_diag_lock_handler()函数以req->sdiag_family值作为索引进行访问。索引来自userland。调用的函数代码是:

代码语言:javascript
复制
static const inline struct sock_diag_handler *sock_diag_lock_handler(int family)
{
        if (sock_diag_handlers[family] == NULL)
                request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
                                NETLINK_SOCK_DIAG, family);

        mutex_lock(&sock_diag_table_mutex);
        return sock_diag_handlers[family];
}

因此,我们可以看到,提供的索引直接用于访问名为sock_diag_handlers[]的数组,而无需检查数组长度。该数组包含40个元素,但利用程序中的索引为0x37,即55 -因此代码读取超过数组结束的16个条目的字节(即128个字节,因为每个条目都是一个指针,并且攻击显然是针对64位机器的)。

调用方(__sock_diag_rcv_msg())将返回的值用作指向结构的指针,该结构特别包含一个称为dump()的字段,该字段是函数指针--代码在指针后面调用该函数。

该漏洞依赖于内核内存空间中在数组的偏移量55处存在一个合适的指针(不是数组的一部分,而是恰好存在于其中的一些其他数据)。该指针指向内核空间中的良性和正常结构,这意味着完全不同;但是错误的代码将将该结构解释为struct sock_diag_handler,并读取它假定为fault字段的内容,该字段恰好包含0x1A000值。所以代码会“跳转”到那个地址。

漏洞攻击所剩下的就是确保在0x1A000地址上有可执行代码。该地址是用户空间的一部分;userland漏洞可以通过mmap()调用保留该空间,并在其中放置一些代码。在这里,使当前正在执行的进程"root“的代码。

需要理解的重要一点是“用户空间”和“内核空间”并存。当它执行一个进程时,它将内存“视为”页面的集合,其中一些页被映射到实际的RAM中。“内核空间”是这些页面的子集。映射是用MMU完成的,它强制执行访问权限:当执行的代码具有“用户权限”(与“内核权限”相反;这里我们不是在讨论根和非根)时,任何访问标记为“内核空间”的页面的尝试都会触发异常(分段错误)。当进程执行系统调用(例如,send()调用)时,它跳入内核空间并暂时获得内核权限,此时所有内核空间都可以访问。但是页面的映射并没有改变;用户土地页仍然存在,关键的是,虽然用户权限不足以访问内核页面,但是内核特权足以访问用户页面。因此,没有什么可以阻止具有内核特权的代码跳入用户空间中的代码。这就是这里发生的事。

因此,开发控制流是:

  • 进程在地址0x1A000处设置其特殊代码。
  • 进程使用一些参数执行send()系统调用,这些参数导致在内核空间以内核权限执行__sock_diag_rcv_msg()
  • 该函数错误地遵循一个函数指针(但不是),并跳入地址0x1A000的代码。该代码位于用户空间(即它是由利用漏洞构建的,具有完全合法的mmap()调用),但以内核权限执行。
  • 该漏洞代码使用内核特权来更改正在执行的代码的当前“标识”。该标识( UID )只是内核空间中某个表中的一个值;由于代码具有内核权限,它可以简单地将UID更改为0,这意味着根。
  • 利用漏洞的代码返回,所以执行返回给__sock_diag_rcv_msg(),他对整个过程非常满意,然后返回。最终,系统调用终止,用户土地进程以用户权限重新获得控制。
  • 但是,内核现在认为进程作为root运行。使用这些新获得的权限,利用漏洞代码只会启动一个shell。

0x37和0x1A000只是在特定内核版本的情况下“工作得很好”的值,该漏洞是在特定的内核版本中定制的:攻击者注意到,在内核空间中,在溢出数组之后128个字节的内核空间中,有一个指向内核空间结构的指针,该结构的伪dump字段包含一个值,该值被解释为指针,指向的地址足够低,足以到达普通用户mmap()。我们在这里讨论的数据(超限数组及其在RAM中的结构)是“常量数据”段的一部分:这些是在编译内核时填充的正常、常量的数据结构。因此,在编译新内核时,它们可能会发生变化,但受影响内核的所有实例在这方面都是相同的--这就是为什么漏洞是特定于内核版本的原因(这里是Ubuntu 12.10以64位模式提供的版本),但是将在所有使用该内核的系统上工作。

其他受影响的内核版本或具有不同选项的其他编译可能需要为sdiag_familymmap_start提供不同的值,但适当结构上的条件并不困难,而且可能存在许多其他适当的组合。(或者至少存在,因为内核现在已经在这方面进行了修复,并且不会在数组结束后进行任何访问,从而避免了整个问题。)

票数 2
EN
页面原文内容由Security提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://security.stackexchange.com/questions/95850

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档