“云分析”,实际调试出现问题,过一段时间再弄。。。
初次接触内核漏洞分析,如有错误还请指正。本文所有代码均来自linux kernel 3.3.3

影响范围linux kernel 3.3.3-3.3.8

原因

sock_diag_lock_handler函数在处理接受的数据时,没有根据数组边界判断大小,导致数组越界,从而导致任意代码执行。

static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
    int err;
    struct sock_diag_req *req = NLMSG_DATA(nlh);
    struct sock_diag_handler *hndl;

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

    hndl = sock_diag_lock_handler(req->sdiag_family);//没有对req->sdiag_family大小做判断
    if (hndl == NULL)
        err = -ENOENT;
    else
        err = hndl->dump(skb, nlh);//任意代码执行
    sock_diag_unlock_handler(hndl);

    return err;
}
----------------------------------------------------------------------------------------------------------
static 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];//根绝family的值返回数组中指定的元素
}
----------------------------------------------------------------------------------------------------------
struct sock_diag_handler {
    __u8 family;
    int (*dump)(struct sk_buff *skb, struct nlmsghdr *nlh);
};

如何触发该漏洞

这个漏洞出现在sock_diag中,google的时候找到了这个页面sock_diag[1]: http://man7.org/linux/man-pages/man7/sock_diag.7.html 从描述可知,它是netlink的一种机制,关于netlink

netlink,一种特殊的socket,用于内核与用户空间进行双向的数据传输(通信)。
这也就意味着,我们在使用netlink的时候就有机会触发这个漏洞,首先需要自己构建netlink消息

构建netlink消息的数据包

netlink消息结构

首先需要nlmsghdr作为报头

nlmsghdr在linux kernel中的定义

struct nlmsghdr {
    __u32        nlmsg_len;    /* Length of message including header */
    __u16        nlmsg_type;    /* Message content */
    __u16        nlmsg_flags;    /* Additional flags */
    __u32        nlmsg_seq;    /* Sequence number */
    __u32        nlmsg_pid;    /* Sending process port ID */
};

在执行__sock_diag_rcv_msg函数之前会执行sock_diag_rcv_msg函数,通过nlmsg_type选择,相关代码

static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
    int ret;

    switch (nlh->nlmsg_type) {
    case TCPDIAG_GETSOCK:
    case DCCPDIAG_GETSOCK:
        if (inet_rcv_compat == NULL)
            request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
                    NETLINK_SOCK_DIAG, AF_INET);

        mutex_lock(&sock_diag_table_mutex);
        if (inet_rcv_compat != NULL)
            ret = inet_rcv_compat(skb, nlh);
        else
            ret = -EOPNOTSUPP;
        mutex_unlock(&sock_diag_table_mutex);

        return ret;
    case SOCK_DIAG_BY_FAMILY:
        return __sock_diag_rcv_msg(skb, nlh);
    default:
        return -EINVAL;
    }
}

因此,nlmsg_type需要为SOCK_DIAG_BY_FAMILY才可以。

pad部分实际上是为了内存对齐
payload

利用分析

利用思路:构造msg->sdiag_family,使得sock_diag_handlers[msg->sdiag_family]的地址中相应的dump偏移的值指向用户态内存空间。exlpoit-database给出的exploit中使用了一个结构体netlink_table,该结构体中有一个成员变量nl_portid_hash,也是一个结构体,而nl_portid_hash中有一个成员变量unsigned long rehash_time的值范围:0x10000-0x130000(对应的内存地址范围为用户态),可将payload映射在0x10000-0x130000区域。

  • 利用mmap在0x10000-0x130000写入payload,利用“滑雪板”的方式,将该范围的低地址写入0x90,高地址写入payload
  • 获取内核符号信息,包括执行提权的commit_creds,prepare_kernel_cred,以及sock_diag_handlers和netlink_table的内核地址。通过计算sock_diag_handlers和netlink_table的“距离差”,计算偏移(msg->sdiag_family的值),使得越界后指向netlink_table结构体中的目标成员(dump)内存地址。
  • 向内核发送socket数据send(triger, &msg, sizeof(msg), 0),触发内核将rehash_time的值作为hndl->dump函数的入口地址,跳转到用户态部署的payload执行提权操作。

相关链接

preView